二分搜索在旋转排序列表中查找旋转点
我有一个已排序的列表,该列表已旋转,并且想对该列表进行二分搜索以查找最小元素。
假设初始列表是 {1,2,3,4,5,6,7,8} 旋转列表可以像 {5,6,7,8,1,2,3,4}
正常的二分搜索在这种情况下不起作用。知道如何做到这一点。
-- 编辑
我还有另外一个条件。如果列表未排序怎么办?
I have a sorted list which is rotated and would like to do a binary search on that list to find the minimum element.
Lets suppose initial list is {1,2,3,4,5,6,7,8}
rotated list can be like {5,6,7,8,1,2,3,4}
Normal binary search doesn't work in this case. Any idea how to do this.
-- Edit
I have one another condition. What if the list is not sorted??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
您只需要对二分搜索算法稍作修改即可;这是完整的可运行Java解决方案(请参阅Serg 对 Delphi 实现的回答,以及 tkr 的答案用于算法的直观解释)。
打印:
另请参阅
Integer[]
而不是int[]
>>> 1
而不是/ 2
关于重复项
请注意,重复项使得不可能在
O(log N)
中完成此操作。考虑以下由许多1
和一个0
组成的位数组:该数组可以以
N
种方式旋转,并定位
是不可能的,因为无法判断它是在“中间”的左侧还是右侧。O(log N)
中的 0然后,除非您想先对其进行排序并从那里继续,否则您必须进行线性搜索才能找到最小值。
另请参阅
A slight modification on the binary search algorithm is all you need; here's the solution in complete runnable Java (see Serg's answer for Delphi implementation, and tkr's answer for visual explanation of the algorithm).
This prints:
See also
Integer[]
instead ofint[]
>>> 1
instead of/ 2
On duplicates
Note that duplicates makes it impossible to do this in
O(log N)
. Consider the following bit array consisting of many1
, and one0
:This array can be rotated in
N
ways, and locating the0
inO(log N)
is impossible, since there's no way to tell if it's in the left or right side of the "middle".Then, unless you want to sort it first and proceed from there, you'll have to do a linear search to find the minimum.
See also
下面是一张图片来说明建议的算法:
Here is a picture to illustrate the suggested algorithms:
我想对该列表进行二分搜索以找到最小元素。
三元搜索适用于这种情况:当函数恰好有一个局部最小值时。
http://en.wikipedia.org/wiki/Ternary_search
编辑
在第二次阅读时,我可能误解了这个问题:函数不符合三元搜索的要求:/但是二元搜索不起作用吗?假设原始订单正在增加。
I would like to do a binary search on that list to find the minimum element.
Ternary search will work for such case: when function has exactly one local minimum.
http://en.wikipedia.org/wiki/Ternary_search
edit
Upon second reading, I probably misunderstood the question: function does not conform requirements for ternary search :/ But won't binary search work? Suppose, original order was increasing.
只需在
list - list[end]
上执行二分方法即可范围 [1, 结束)。二分法通过搜索符号变化来查找函数中的零,其运算时间复杂度为 O(log n)。例如,
{5,6,7,8,1,2,3,4}→ {1,2,3,4,-3,-2,-1,0}
然后对该列表 {1,2,3,4,-3,-2,-1} 使用(离散)二分法。它将找到 4 和 -3 之间的零交叉点,该交叉点对应于您的旋转点。
Just perform the bisection method on
list - list[end]
over the range [1, end). The bisection method looks for zeros in a function by searching for a sign change, and operates in O(log n).For example,
{5,6,7,8,1,2,3,4} -> {1,2,3,4,-3,-2,-1,0}
Then use the (discretized) bisection method on that list {1,2,3,4,-3,-2,-1}. It will find a zero crossing between 4 and -3, which corresponds to your rotation point.
Delphi 版本 - 第三个改进(感谢 Polygenelubricants 代码 - 但又删除了一个比较)变体:
Delphi version - third improved (thanks to polygenelubricants code - yet one more comparison removed) variant:
从列表
[first, last)
中选取一些子序列[i,j]
。[i,j]
不包含不连续性,在这种情况下*i <= *j
,或者包含不连续性,在这种情况下剩余元素(j, last) U [first, i)
已正确排序,在这种情况下*j <= *i
。递归地二分可疑范围,直到筛选出一个元素。进行 O(log N) 比较。
Pick some subsequence
[i,j]
of the list[first, last)
. Either[i,j]
does not contain the discontinuity, in which case*i <= *j
, or it does, in which case the remaining elements(j, last) U [first, i)
, are properly sorted, in which case*j <= *i
.Recursively bipartition the suspect range until you winnow down to one element. Takes O(log N) comparisons.
我在 Java 中实现的二分搜索算法版本:
代码成功通过了 5000 个测试用例,所以我认为它已经准备好投入生产了。
My version of binary search algorithm implementation in Java:
Code successfully passed 5000 TestCases, so I think it's production ready.
如果我们想保持代码的简单性和可读性,递归是非常好的。但如果我们能够避免递归并仍然保持可读性,那就更好了,因为递归成本很大并且实际上不可扩展。
这是一个简单的迭代方法,其逻辑与上面讨论的非常相似(它利用二分搜索,添加小分区逻辑)。
Recursion is very good if we want to maintain simplicity and readability of the code. But if we can avoid recursion and still maintain the readability, it would be better because recursion cost is significant and not actually scalable.
Here is a simple iterative method with logic pretty much as discussed above ( it's taking advantage of binary search, adding small partition logic).
在 C++ 11 中,这个问题可以通过 partition_point 来解决:
In C++ 11, this problem can be solved with partition_point:
在 C++ 中,可以使用此代码( O(log(n)) )来获取旋转排序列表中的旋转次数:
如果列表未排序,您应该知道数组最初是什么,并且可以线性检查为旋转点 ( O(n) )。
In C++, one can use this code ( O(log(n)) ) to get the number of rotations in a rotated sorted list:
In case the list is not sorted, you should know what the array was originally and you can check linearly for the point of rotation ( O(n) ).
像这样的东西可能会起作用(未经测试):
Something like this might work (Not tested):