【问题标题】:Find an element in an infinite length sorted array在无限长的排序数组中查找元素
【发布时间】:2012-08-31 07:43:38
【问题描述】:

给定一个包含正整数和负整数的无限长排序数组。在其中找到一个元素。

编辑
数组中的所有元素都是唯一的,并且数组在正确的方向上是无限的。

有两种方法:

方法一:

在位置100设置索引,如果要查找的元素较少,则在前100项中进行二分查找,否则将下一个索引设置在位置200。这样,不断增加索引100,直到该项更大。

方法二:

以 2 的幂设置索引。首先将索引设置在位置 2,然后是 4,然后是 8,然后是 16,依此类推。再次从位置 2^K 到 2^(K + 1) 进行二分搜索,其中 item 介于两者之间。

在最佳情况和最坏情况下,这两种方法中哪一种更好?

【问题讨论】:

  • 在处理无限集合时,“更好”是什么意思?你追求什么样的渐近线?数组在两个方向上是无限的,还是有最小的元素?元素是否独特?
  • 考虑到真正的无限长度,它没有任何区别——除了偶然之外,没有人有任何成功找到元素的希望。无论您如何搜索,您能够搜索的数组的百分比是数组的某个有限数 N / infinity = 0%。
  • @JerryCoffin:还要注意,即使数组两边都是无限的,也可以很容易地简化为单边无限数组:访问任意索引(让它成为k)。 if x < arr[k] :在索引为(-infninity,k] 的一侧无限数组中搜索。否则,搜索[k,infinity)。唯一的问题是真正的唯一性和非 Zeno 属性(整数保证)
  • 如果它有助于避免让任何人感到困惑,可以将“无限排序的唯一值数组”替换为“自然数的单调递增函数,由恒定时间的预言机计算。”
  • 您想在时间复杂度计算中加入索引数学运算花费的时间超过 O(1) 时间吗?

标签: performance algorithm data-structures time-complexity


【解决方案1】:

您可以或多或少地直接应用二进制搜索,只需稍作修改。这将大致对应于您的方法 2。

基本上,选择一些数字 B 并将 A 设置为 0,然后检查您要查找的元素是否介于 A 和 B 之间。如果是,则在这些边界中执行通常的二分搜索,否则设置 B= A 和 A=2*A 并重复。这将花费 O(log(M)),其中 M 是您要在数组中查找的元素的位置。

【讨论】:

    【解决方案2】:

    第一种方法在元素的索引中是线性的(O(k) 其中k 是元素的索引)。实际上,您将需要k/100 迭代才能找到大于搜索元素的元素,即O(k)

    第二种方法将在同一索引中对数。 O(logk)。 (其中k 是元素的索引)。在这里,您将需要log(k) 迭代,直到找到更高的元素。那么2^(i-1)2^i(其中i是迭代次数)之间的二分搜索也将是对数的,总计为O(logk)

    因此,第二种效率更高

    【讨论】:

    • 我感觉这两种方法都与O(n) 相同。你能看看我的回答stackoverflow.com/a/12312488/538514吗?
    • @ViktorStolbin:不,是O(logn) - 我评论了你的回答为什么。
    • @amit,如果您需要 logn 迭代次数和另一个 logn 每个 logn 迭代的搜索次数,为什么是 O(logn)?那不是O(log^2(n))吗?
    • @czchlong:只有在最后一次迭代之后才需要进行二分查找,到那时再查找一个值是没有意义的,因为它不会在这个范围内。第一次 i-1 次迭代只是检查是否x < arr[2^i],如果是 - 前进到下一次迭代,没有任何二进制搜索。
    • @amit,哈哈,我明白你现在在说什么了。谢谢。
    【解决方案3】:

    如果数组是有根据的,即有一个最小元素(即你有元素x0, x1 , ...),并且所有元素都是唯一的,那么这里有一个简单的方法:如果你正在寻找数字 n,你可以对索引 0 进行二进制搜索, ...,n - x0。请注意,我们总是有基本的不等式 xi ≥ i + x0 对于所有 i ≥ 0。

    因此您可以在 log2(n − x0) 步骤。

    【讨论】:

    • 有了这个上限,实际上数组不再是无限的,问题变得更像是“以下哪个修改后的二进制搜索更好?” (1) (我认为)显然是一种愚蠢的搜索方式,因为它是由线性部分主导的混合线性二进制搜索。 (2) 可能对简单的二分搜索有话要说:如果您的数据通常是稀疏的(很多缺失的元素),那么您会期望边界被大大高估,并且目标接近开始,因此对于足够稀疏的数据 (2) 进行的比较较少。
    • @SteveJessop:问题的症结在于您对算法的运行时间没有任何先验界限。运行时间取决于针的值。但事实上,我们唯一可以问的是给一根针,在大海捞针中找到它的最有效方法是什么?
    • 是的,但针的值受输入数据的非无限部分的大小限制,(2^N,N 是输入的位数)。因此,就像任何其他复杂性分析一样,当我们声明O(f(n)) 时,我们只需要清楚n 的含义。另请参阅当有人说“好消息,他们已经证明 ISPRIME 在 P 中”而其他人说“什么?显然是,sqrt(n) 试验部门”时的困惑;-)
    • @SteveJessop:是的,相当——这就是为什么我希望 OP 在表述问题和描述他所追求的方面更加小心。 :-)
    • @SteveJessop @KerrekSB:如果你想让它更有趣,假设一个具有非 zino 属性的 real 数字的无限排序数组(对于每个 nR 中,有一个有限索引i 使得arr[i] > n)。我认为通过这种修改,OP 的第二种方法更适合。
    【解决方案4】:

    只有我的 2 美分。我们有一个无限数组,因此让我们想象我们正在寻找非常大的数字。你想象过吗?好吧,它变得更大了。请注意,二进制搜索的间隔长度是2^i = 2^(i+1)-2^i,因此它应该花费log(2^i)=i 时间来查找数字。另一方面,达到目标间隔需要i 时间。所以总时间复杂度又是O(n)。我错过了什么?

    【讨论】:

    • 第二种方法是O(logn),其中n是所需元素的Index。请注意,二分查找确实在[2^i,2^(i+1)] 范围内完成,因此O(i),但i 本身是O(logn),因为2^(i+1) > n => i+1 > logn,因此O(i) 实际上是O(logn)
    • 是的,我对n 的含义感到困惑。换句话说,我的答案与O(i) 相同,其中i=logn。谢谢
    【解决方案5】:

    由于数组是无限的,索引必然是可变长度的。这意味着对它们进行数学运算不是O(1),这反过来意味着“首先搜索端点的二进制搜索”的时间复杂度与O(log(k))略有不同。

    在搜索端点时完成的索引数学运算只是左移一位,这需要O(log(k)),因为直到k 的索引需要最多log(k) 位,并且左移一位在数字中是线性的位。

    在二分搜索中完成的索引数学也是O(log(k))

    所以这两种算法的实际复杂度是O(log(k)^2)。线性搜索的复杂度是O(k log k),所以还是输了。

    【讨论】:

      【解决方案6】:

      Here 是另一种实现,它使用 2^n 来搜索元素本身的出现,然后将此子数组进行二分搜索

      e.g.
       arr = 1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1...........
       num = 8;
      
      efficiency = 2logn
      

      【讨论】:

        【解决方案7】:

        --完全解决方案--时间复杂度为O(logn)

        public class B {
        
            public static void main(String[] args) {
                // Assuming sorted array of infinite length
                int a[] = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
        
                int elementToFind = 12;
        
                int finalIndex = getFinalIndex(a, elementToFind);
        
                if (finalIndex == -1) {
                    System.out.println("Element not found");
                }
                System.out.println("Found element:" + a[finalIndex]);
        
            }
        
            private static int getFinalIndex(int[] a, int elementToFind) {
        
                int power = 2;
                int finalIndex = (int) Math.pow(2, power);
        
                for (int i = 0; i < finalIndex;) {
        
                    if (elementToFind == a[finalIndex]) {
                        return finalIndex;
                    }
        
                    else if (elementToFind < a[finalIndex]) {
                        System.out.println("search through binary search algo");
                        // taking i as starting index in binary search call
                        int searchedIndex = callToBinarySearch(a, i, finalIndex);
                        return searchedIndex;
                    }
        
                    else {
                        i = finalIndex + 1;
                        power = power * 2;
                        finalIndex = (int) Math.pow(2, power);
        
                    }
                }
                return -1;
        
            }
        }
        

        【讨论】:

          【解决方案8】:

          包 com.population.app;

          导入 java.io.; 导入 java.util.;

          类演示{

          public static void main(String args[]) {
              int arr[] = { 1, 2, 4, 6, 8, 9, 12, 14, 17, 21, 45 };
              int index = findPos(arr, 45);
              if (index == -1)
                  System.out.println("Element not found!");
              else
                  System.out.println("Element found! index = " + index);
          }
          
          static int findPos(int arr[], int value) {
              int start = 0, end = 1;
              while (arr[end] < value) {
                  start = end;
                  end = 2 * end;
                  // we know it is infinite but if it has finite elements it will reduce the
                  // overflow of legth to n-1 element other wise the code will fail has array
                  // index out of bound exception
                  if (end > arr.length) {
                      end = arr.length - 1;
                  }
              }
              return binarySearch(arr, start, end, value);
          }
          
          static int binarySearch(int arr[], int start, int end, int ele) {
              if (end >= start) {
                  int mid = start + (end - start) / 2;
                  if (arr[mid] == ele)
                      return mid;
                  if (arr[mid] > ele)
                      return binarySearch(arr, start, mid - 1, ele);
                  return binarySearch(arr, mid + 1, end, ele);
              }
              return -1;
          }
          

          }

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-03-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-07
            • 1970-01-01
            • 1970-01-01
            • 2017-06-07
            相关资源
            最近更新 更多