我的问题与Q1和Q2非常相似,只是我想处理数组可能有重复条目的情况。
假设数组A由按升序排序的整数组成。如果它的条目都是不同的,您可以使用二进制搜索在O(log n)中轻松完成。但是如果有重复的条目,那就更复杂了。这是我的方法:
int search(const vector<int>& A) {
int left = 0, right = A.size() - 1;
return binarySearchHelper(A, left, right);
}
int binarySearchHelper(const vector<int>& A, int left, int right) {
int indexFound = -1;
if (left <= right) {
int mid = left + (right - left) / 2;
if (A[mid] == mid) {
return mid;
} else {
if (A[mid] <= right) {
indexFound = binarySearchHelper(A, mid + 1, right);
}
if (indexFound == -1 && A[left] <= mid) {
indexFound = binarySearchHelper(A, left, mid - 1);
}
}
}
return indexFound;
}
在最坏的情况下(A没有等于其索引的元素),binarySearchHelper
进行2次递归调用,在每个递归级别输入大小减半,这意味着它的最坏情况时间复杂度为O(n )。这与O(n)方法相同,您只需按顺序读取数组。这真的是你能做的最好的吗?另外,有没有办法测量算法的平均时间复杂度?如果没有,是否有一些启发式方法来决定何时使用基本的O(n)直读方法以及何时尝试递归方法,例如我的?
如果A有负整数,则需要检查if (left <= right)
中的条件binarySearchHelper
。例如,如果A = [-1]
,那么算法会从bsh(A, 0, 0)
递归到bsh(A,1,0)
和bsh(A,0,-1)
。我的直觉使我相信支票if (left <= right)
是必要的,当且仅当A有一些负整数。任何人都可以帮我核实一下吗?
答案 0 :(得分:2)
我会采取不同的方法。首先,我将通过对第一个正数进行二进制搜索来消除 O(log n)中的所有负数。这是允许的,因为没有负数可以等于其索引。让我们说第一个正元素的索引是i
。
现在我将继续执行以下操作,直到找到该元素或发现它不存在:
i
不在A
内,则返回false。i < A[i]
做i = A[i]
。需要A[i] - i
重复才能让i
赶上&#39;到A[i]
,我们会i
增加A[i] - i
,这相当于将i
设置为A[i]
。转到1. i == A[i]
返回true(如果您愿意,则返回索引)。i
的第一个索引i <= A[i]
。你可以这样做从左边做一个二进制搜索&#39;通过将i
递增1,2,4,8等,然后在找到它的最后一个间隔进行二进制搜索。如果它不存在,则返回false。在最糟糕的情况下,上面的结论是 O(n),但是在更好的情况下,它有很多技巧可以加快速度。