在循环排序数组中搜索元素
我们希望在循环排序数组中搜索给定元素,其复杂度不大于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);
}
}
希望这会起作用。
We want to search for a given element in a circular sorted array in complexity not greater than O(log n)
.
Example: Search for 13
in {5,9,13,1,3}
.
My idea was to convert the circular array into a regular sorted array then do a binary search on the resulting array, but my problem was the algorithm I came up was stupid that it takes O(n)
in the worst case:
for(i = 1; i < a.length; i++){
if (a[i] < a[i-1]){
minIndex = i; break;
}
}
then the corresponding index of ith element will be determined from the following relation:
(i + minInex - 1) % a.length
it is clear that my conversion (from circular to regular) algorithm may take O(n), so we need a better one.
According to ire_and_curses idea, here is the solution in 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);
}
}
Hopefully this will work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
下面是使用二分搜索的 C 实现。
Below is a implementation in C using binary search.
这是 javascript 中的解决方案。用几个不同的阵列对其进行了测试,它似乎有效。它基本上使用 ire_and_curses 描述的相同方法:
Here's a solution in javascript. Tested it with a few different arrays and it seems to work. It basically uses the same method described by ire_and_curses:
简单的二分查找,稍加改动。
旋转数组的索引=(i+pivot)%大小
pivot是索引i+1,其中a[i]>a[i+1]。
Simple binary search with a little change.
Index of rotating array= (i+pivot)%size
pivot is the index i+1 where a[i]>a[i+1].
Ruby 中的一个简单方法
A simple method in
Ruby
虽然批准的答案是最佳的,但我们也可以使用类似且更清晰的算法。
O(logn)
O(logn)
O(logn)
总时间复杂度:
O(logn)
欢迎思考。
Though approved answer is optimal but we can also do with a similar and cleaner algorithm.
O(logn)
O(logn)
O(logn)
Total Time Complexity:
O(logn)
Thoughts welcome.
guirgis:发布面试问题很蹩脚,猜猜你没有得到这份工作:-(
使用特殊的 cmp 函数,你只需要常规二分搜索一次。类似于:
如果你可以依赖 int 下溢减去 a [0] - 访问每个元素时的 MIN_INT 并使用常规比较。
guirgis: It is lame to post an interview question, guess you didn't get the job :-(
Use a special cmp function and you need only one pass with regular binary search. Something like:
If you can depend on int underflow subtract a[0] - MIN_INT from each element as it is accessed and use regular compare.
您可以通过利用数组已排序这一事实来实现此目的,但主值及其邻居值之一的特殊情况除外。
a[0] < a[mid]
,则所有值数组的前半部分是
已排序。
a[mid] < a[最后]
,然后是所有下半年的数值
数组已排序。
一半,然后检查你的值是否
位于其中(与
那一半的最大 idx)。
搜索那一半。
必须在未排序的一半中。拿
那一半并重复这个过程,
确定那一半的哪一半
已排序等
You can do this by taking advantage of the fact that the array is sorted, except for the special case of the pivot value and one of its neighbours.
a[0] < a[mid]
, then all values inthe first half of the array are
sorted.
a[mid] < a[last]
, then allvalues in the second half of the
array are sorted.
half, and check whether your value
lies within it (compare to the
maximum idx in that half).
search that half.
must be in the unsorted half. Take
that half and repeat this process,
determining which half of that half
is sorted, etc.
不是很优雅,但最重要的是 - 只需使用二分搜索来找到旋转数组的主元,然后再次执行二分搜索,补偿主元的偏移量。执行两次完整搜索有点愚蠢,但它确实满足条件,因为 O(log n) + O(log n) == O(log n)。保持简单和愚蠢(tm)!
Not very elegant, but of the top off my head - just use binary search to find the pivot of the rotated array, and then perform binary search again, compensating for the offset of the pivot. Kind of silly to perform two full searches, but it does fulfill the condition, since O(log n) + O(log n) == O(log n). Keep it simple and stupid(tm)!
这是一个在 Java 中运行的示例。由于这是一个排序数组,因此您可以利用它并运行二分搜索,但是需要对其进行稍微修改以适应枢轴的位置。
该方法如下所示:
现在为了缓解任何担忧,这里有一个很好的小类来验证算法:
它会给您一个输出:
This is an example that works in Java. Since this is a sorted array you take advantage of this and run a Binary Search, however it needs to be slightly modified to cater for the position of the pivot.
The method looks like this:
Now to ease any worries, here's a nice little class that verifies the algorithm:
That give you an output of:
您有三个值:
l
、m
、h
,分别表示搜索的低、中、高索引值。如果您认为您会继续搜索每种可能性:这是一个考虑目标值可能在哪里并搜索那一半空间的问题。最多一半的空间会有包裹,很容易判断目标值是在一半还是另一半。
这是一个元问题 - 您是否认为二分搜索通常是如何呈现的 - 查找两点之间的值,或者更一般地说,作为抽象搜索空间的重复划分。
You have three values,
l
,m
,h
for the values at the low, mid and high indexes of your search. If you think were you would continue searching for each possibility:It's a question of considering where the target value could be, and searching that half of the space. At most one half of the space will have the wrap-over in it, and it is easy to determine whether or not the target value is in that half or the other.
It's sort of a meta question - do you think of binary search it terms of how it is often presented - finding a value between two points, or more generally as a repeated division of an abstract search space.
您可以使用二分查找来查找最小元素的位置并将其减少到O(Log n)。
您可以通过以下方式找到位置(这只是算法的草图,它不准确,但您可以从中得到想法):
1. i <- 1
2. j <- n
3. 当我< j
3.1. k <- (ji) / 2
3.2.如果 arr[k] < arr[i] 则 j <- k
3.3. else i <- k
找到最小元素的位置后,您可以将数组视为两个排序数组。
You can use binary search to find the location of smallest element and reduce it to O(Log n).
You can find the location by (this is just a sketch of algorithm, it's inaccurate but you can get the idea from it):
1. i <- 1
2. j <- n
3. while i < j
3.1. k <- (j-i) / 2
3.2. if arr[k] < arr[i] then j <- k
3.3. else i <- k
After finding the location of the smallest element you can treat the array as two sorted arrays.
您只需使用简单的二分搜索,就像它是常规排序数组一样。唯一的技巧是您需要旋转数组索引:
其中起始索引是循环数组中第一个元素的偏移量。
You just use a simple binary search as if it were a regular sorted array. The only trick is you need to rotate the array indexes:
where the start-index is the offset of the first element in the circular array.
检查这个coe,
Check this coe,
这是一个与二分搜索相关的想法。只需继续备份右数组索引边界的索引,左索引边界存储在步长中:
为了避免 (pos==1) 的情况,我们可以循环备份(进入负数)并采取 ( pos-1) 模 n。
Here is an idea, related to binary search. Just keep backing up your index for the right array-index bound, the left index bound is stored in the step size:
To avoid the (pos==1) thing, we could back up circularly (go into negative numbers) and take (pos-1) mod n.
我认为您可以使用以下代码找到偏移量:
I think you can find the offset using this code: