【发布时间】:2011-02-19 13:12:13
【问题描述】:
我们想在复杂度不大于O(log n)的循环排序数组中搜索给定元素。
示例:在{5,9,13,1,3} 中搜索13。
我的想法是将循环数组转换为常规排序数组,然后对结果数组进行二进制搜索,但我的问题是我提出的算法很愚蠢,在最坏的情况下它需要 O(n):
for(i = 1; i < a.length; i++){
if (a[i] < a[i-1]){
minIndex = i; break;
}
}
那么第i个元素的对应索引将由以下关系确定:
(i + minInex - 1) % a.length
很明显,我的转换(从循环到常规)算法可能需要 O(n),所以我们需要一个更好的。
根据 ire_and_curses 的想法,这里是 Java 中的解决方案:
public int circularArraySearch(int[] a, int low, int high, int x){
//instead of using the division op. (which surprisingly fails on big numbers)
//we will use the unsigned right shift to get the average
int mid = (low + high) >>> 1;
if(a[mid] == x){
return mid;
}
//a variable to indicate which half is sorted
//1 for left, 2 for right
int sortedHalf = 0;
if(a[low] <= a[mid]){
//the left half is sorted
sortedHalf = 1;
if(x <= a[mid] && x >= a[low]){
//the element is in this half
return binarySearch(a, low, mid, x);
}
}
if(a[mid] <= a[high]){
//the right half is sorted
sortedHalf = 2;
if(x >= a[mid] && x<= a[high] ){
return binarySearch(a, mid, high, x);
}
}
// repeat the process on the unsorted half
if(sortedHalf == 1){
//left is sorted, repeat the process on the right one
return circularArraySearch(a, mid, high, x);
}else{
//right is sorted, repeat the process on the left
return circularArraySearch(a, low, mid, x);
}
}
希望这会奏效。
【问题讨论】:
-
你应该澄清你是否事先知道循环数组从哪里开始。通常在您会知道的实际应用程序中。
-
不,我不知道循环数组从哪里开始,如果我知道,那么我不需要转换算法,而是直接应用上述关系并进行二进制搜索。
-
您需要知道元素是否不同。否则最坏的情况是 Omega(n)。
-
考虑到问题的限制,它从来没有说数组是排序的,也没有给出任何部分排序的建议。使用排序转换数组最多会使您减少 n 次(计算具有额外空间复杂度的排序),并且在大多数情况下为 nlgn 时间。数组可能完全未排序,使得二分搜索方案无法使用(对于 lgn 时间)。请参阅 stackoverflow.com/a/7694019/2812818
标签: algorithm binary-search circular-buffer