在小于线性的时间内,在排序数组中找到重复项
Posted
技术标签:
【中文标题】在小于线性的时间内,在排序数组中找到重复项【英文标题】: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]
(没有10
、11
)只是示例还是一般规则?
【参考方案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
由于lower
和upper
之间的数组在每次迭代中减半,因此算法的运行时间为 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;
【讨论】:
对不起,这不是线性时间。应该使用二分查找来达到目的。以上是关于在小于线性的时间内,在排序数组中找到重复项的主要内容,如果未能解决你的问题,请参考以下文章