Binary search to find the rotation point in a rotated sorted list
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.
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.
if (f(left) < f(middle))
// which means, 'left' and 'middle' are on the same segment (before or after point X we search)
// and also 'left' is before X by definition
// so, X must be to the right from 'middle'
left = middle
else
right = middle
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).
import java.util.*;
public class BinarySearch {
static int findMinimum(Integer[] arr) {
int low = 0;
int high = arr.length - 1;
while (arr[low] > arr[high]) {
int mid = (low + high) >>> 1;
if (arr[mid] > arr[high]) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
public static void main(String[] args) {
Integer[] arr = { 1, 2, 3, 4, 5, 6, 7 };
// must be in sorted order, allowing rotation, and contain no duplicates
for (int i = 0; i < arr.length; i++) {
System.out.print(Arrays.toString(arr));
int minIndex = findMinimum(arr);
System.out.println(" Min is " + arr[minIndex] + " at " + minIndex);
Collections.rotate(Arrays.asList(arr), 1);
}
}
}
This prints:
[1, 2, 3, 4, 5, 6, 7] Min is 1 at 0
[7, 1, 2, 3, 4, 5, 6] Min is 1 at 1
[6, 7, 1, 2, 3, 4, 5] Min is 1 at 2
[5, 6, 7, 1, 2, 3, 4] Min is 1 at 3
[4, 5, 6, 7, 1, 2, 3] Min is 1 at 4
[3, 4, 5, 6, 7, 1, 2] Min is 1 at 5
[2, 3, 4, 5, 6, 7, 1] Min is 1 at 6
See also
- Java Collections.rotate() with an array doesn’t work
- Explains why
Integer[]
instead ofint[]
- Explains why
- Google Research Blog: Nearly All Binary Searches and Mergesorts are Broken
- Explains why
>>> 1
instead of/ 2
- Explains why
On duplicates
Note that duplicates makes it impossible to do this in O(log N)
. Consider the following bit array consisting of many 1
, and one 0
:
(sorted)
01111111111111111111111111111111111111111111111111111111111111111
^
(rotated)
11111111111111111111111111111111111111111111101111111111111111111
^
(rotated)
11111111111111101111111111111111111111111111111111111111111111111
^
This array can be rotated in N
ways, and locating the 0
in O(log N)
is impossible, since there's no way to tell if it's in the left or right side of the "middle".
I have one another condition. What if the list is not sorted??
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
- Wikipedia | Selection algorithm | Linear minimum/maximum algorithms
Here is a picture to illustrate the suggested algorithms: