【问题标题】:In less-than-linear time, find the duplicate in a sorted array在小于线性的时间内,在排序数组中找到重复项
【发布时间】:2012-03-29 05:47:44
【问题描述】:

今天,一位面试官问了我这个问题。我的直接反应是我们可以简单地进行线性搜索,将当前元素与数组中的前一个元素进行比较。然后他问我如何在不到线性的时间内解决这个问题。

假设

  • 数组已排序
  • 只有一个重复
  • 数组填充有数字[0, n],其中n 是数组的长度。

示例数组:[0,1,2,3,4,5,6,7,8,8,9]

我试图提出一个分而治之的算法来解决这个问题,但我不确定它是否是正确的答案。有人有什么想法吗?

【问题讨论】:

  • 您的示例仅包含数字[0,n-2](没有1011)只是示例还是一般规则?

标签: arrays algorithm search language-agnostic data-structures


【解决方案1】:

可以在 O(log N) 中通过修改后的二进制搜索完成:

从数组的中间开始:如果 array[idx]

【讨论】:

  • 通用版应该是array[idx]-array[0]
【解决方案2】:

如果数组中没有缺少任何数字,如示例中所示,则可以使用二进制搜索在 O(log n) 中完成。如果a[i] < i,则重复在i之前,否则在i之后。

如果一个号码缺一个重复,我们仍然知道如果a[i] < i重复必须在i之前,如果a[i] > i,那么缺号码必须在i之前和重复之后。但是,如果是a[i] == i,我们不知道丢失的数字和重复是在i 之前还是在i 之后。在这种情况下,我看不到亚线性算法的方法。

【讨论】:

  • 我很晚了,但是如果你允许丢失数字,那确实是不可能的(假设你不能在 O(1) 中读取任意大量的单元格)。假设我们考虑大小为 n+1 (n>=2) 的条目,并且我们将自己限制在这个条目子集:{[0,0,2,...,n], [0,1,1,3,...,n ], ..., [0,1,...,k,k,k+2,...,n], ..., [0,1,...,n-1,n-1]}。假设您已经知道最多 (n-2) 个单元格的内容,并且它们是成对不同的,仍然至少有 2 种可能性,您无法区分任何一种。因此,您需要至少读取 (n-1) 个单元格来确定哪个数字是重复的。
【解决方案3】:

我试图提出一个分而治之的算法来解决这个问题,但我不确定它是否是正确的答案。

当然,您可以进行二分搜索。

如果arr[i/2] >= i/2,则重复项位于数组的上半部分,否则位于下半部分。

while (lower != upper)
    mid = (lower + upper) / 2
    if (arr[mid] >= mid)
        lower = mid
    else
        upper = mid-1

由于lowerupper 之间的数组在每次迭代中减半,因此算法的运行时间为 O(log n)。

ideone.com demo in Java

【讨论】:

    【解决方案4】:

    给定数组元素的总和与 0 到 n-1 个自然数的总和之间的差异会为您提供重复的元素。 0 到 n-1 个元素的总和为 (N * N-1)/2 示例数组是 [0,1,2,3,4,5,6,7,8,8,9] 0到9个自然数之和是:45 给定数组元素的总和:53 53-45 = 8 哪个是重复元素

    【讨论】:

    • 将所有元素相加是 O(n) - 因此超出预算
    【解决方案5】:
    #include <bits/stdc++.h>
    using namespace std;
    
    int find_only_repeating_element(int arr[] , int n){
    int low = 0;
    int high = n-1;
    while(low <= high){
        int mid = low + (high - low)/2;
        if(arr[mid] == arr[mid + 1] || arr[mid] == arr[mid - 1]){
            return arr[mid];
        }
        if(arr[mid] < mid + 1){
            high = mid - 2;
        }else{
            low = mid + 1;
        }
       }
       return -1;
    }
    
    int main(int argc, char const *argv[])
    {
    int n , *arr;
    cin >> n;
    arr = new int[n];
    for(int i = 0 ; i < n ; i++){
        cin >> arr[i];
    }
        cout << find_only_repeating_element(arr , n) << endl;
        return 0;
    }
    

    【讨论】:

    • 欢迎来到 SO。请考虑在回答问题时为您的代码添加一些解释。
    【解决方案6】:

    那怎么样? (递归风格)

    public static int DuplicateBinaryFind(int[] arr, int left, int right)
    {
       int dup =0;
    
       if(left==right)
       {
          dup = left;
       }
       else
       {
            int middle = (left+right)\2;
            if(arr[middle]<middle)
            {
              dup = DuplicateBinaryFind(arr,left, middle-1);
    
            }
            else
            {
               dup = DuplicateBinaryFind(arr, middle+1, right);
            }
       }
    
       return dup;
    
    }
    

    【讨论】:

      【解决方案7】:

      示例数组与您的问题有点不同。由于 n 是数组的长度,并且数组中只有一个重复项,因此数组中每个元素的值应该在 [0,n-1] 中。

      如果是这样,那么这个问题和How to find a duplicate element in an array of shuffled consecutive integers?是同一个问题

      以下代码应在 O(n) 时间和 O(1) 空间中找到重复项。

      public static int findOnlyDuplicateFromArray(int[] a, boolean startWithZero){
          int xor = 0;
          int offset = 1;
          for(int i=0; i < a.length; i++){
              if(startWithZero)
                  xor = xor ^ (a[i] + offset) ^ i;
              else
                  xor = xor ^ a[i] ^ i;
              }
              if(startWithZero)
                  xor = xor - offset;
          return xor;
      }
      

      【讨论】:

      • 对不起,这不是线性时间。应该使用二分查找来达到目的。
      猜你喜欢
      • 1970-01-01
      • 2019-09-20
      • 2017-01-26
      • 2011-07-22
      • 1970-01-01
      • 2012-01-12
      • 1970-01-01
      • 2015-01-13
      • 2018-05-27
      相关资源
      最近更新 更多