Topics › Binary Search › BS on 1D Arrays
Find out how many times the array is rotated
Java source ↗ Edit this article ↗
Jump to code:
Problem
An array of n distinct integers was sorted in ascending order and then rotated to the right some number of times k, with 0 <= k < n. One right rotation moves the last element to the front. Given the rotated array nums, determine k.
Constraints: 1 ≤ n ≤ 10^5, all values distinct, values fit in a 32-bit signed integer.
Example 1
nums = [3, 4, 5, 1, 2]
answer = 3 // [1, 2, 3, 4, 5] rotated right 3 times
Example 2
nums = [4, 5, 6, 7, 0, 1, 2]
answer = 4 // [0, 1, 2, 4, 5, 6, 7] rotated right 4 times
Example 3
nums = [1, 2, 3, 4, 5]
answer = 0 // not rotated
The key observation turns this into a problem we already know how to solve. Rotating a sorted array right once moves the largest element to index 0 and shifts the minimum from index 0 to index 1. Rotating k times shifts the minimum to index k. So the number of rotations is the index of the minimum element. Check the examples: in [3, 4, 5, 1, 2] the minimum 1 is at index 3, and in [4, 5, 6, 7, 0, 1, 2] the minimum 0 is at index 4.
Approach 1: Brute force, index of the minimum by linear scan
Intuition. Find the position of the smallest element by looking at everything. Start with index 0 as the current best and replace it whenever a smaller value shows up. Distinct values mean the minimum, and therefore its index, is unique.
Algorithm.
minIdx = 0.- For
ifrom1ton - 1: ifnums[i] < nums[minIdx], setminIdx = i. - Return
minIdx.
static int bruteForce(int[] nums) {
int minIdx = 0;
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[minIdx]) minIdx = i;
}
return minIdx;
}
Complexity. Time O(n), space O(1).
Approach 2: Optimal, binary search tracking the index of the sorted half’s minimum
Intuition. A rotated sorted array consists of two ascending runs, and the minimum is the first element of the second run. For any range [lo, hi] with midpoint mid, at least one half is sorted, and nums[lo] <= nums[mid] tells us whether it is the left half [lo, mid]. The first element of a sorted half is that half’s minimum, so we record its index as a candidate and discard the whole half: nothing in it can beat its own first element. The other half contains the drop, so we continue there.
If the entire current range happens to be sorted (nums[lo] <= nums[hi]), its first index lo is the best candidate within the range and we can stop. For a non-rotated array this fires immediately with lo = 0.
The only difference from finding the minimum value is what we remember: the index instead of the value. We still compare values to decide which candidate is better.
Algorithm.
lo = 0,hi = n - 1,minIdx = 0.- While
lo <= hi:mid = lo + (hi - lo) / 2.- If
nums[lo] <= nums[hi]: ifnums[lo] < nums[minIdx],minIdx = lo; stop. - Else if
nums[lo] <= nums[mid]: ifnums[lo] < nums[minIdx],minIdx = lo; thenlo = mid + 1. - Else: if
nums[mid] < nums[minIdx],minIdx = mid; thenhi = mid - 1.
- Return
minIdx.
static int optimal(int[] nums) {
int lo = 0, hi = nums.length - 1, minIdx = 0; // index 0 is a valid first candidate
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (nums[lo] <= nums[hi]) { // whole range sorted: nums[lo] is its minimum
if (nums[lo] < nums[minIdx]) minIdx = lo;
break;
}
if (nums[lo] <= nums[mid]) { // left half sorted: nums[lo] is its minimum, drop it
if (nums[lo] < nums[minIdx]) minIdx = lo;
lo = mid + 1;
} else { // right half sorted: nums[mid] is its minimum, drop it
if (nums[mid] < nums[minIdx]) minIdx = mid;
hi = mid - 1;
}
}
return minIdx;
}
Dry run on nums = [3, 4, 5, 1, 2]:
| lo | hi | mid | nums[lo] <= nums[hi]? | sorted half | candidate | action |
|---|---|---|---|---|---|---|
| 0 | 4 | 2 | 3 <= 2? no | left (3 <= 5) | nums[0] = 3, not < nums[0] | lo = 3 |
| 3 | 4 | 3 | 1 <= 2? yes | whole range | nums[3] = 1 < 3: minIdx = 3 | stop |
Answer 3.
Complexity. Time O(log n), space O(1).
Starting minIdx at 0 is deliberate: nums[0] is a real element, so it is a legitimate initial candidate, and it saves a sentinel. Every later candidate replaces it only if strictly smaller, which is well defined because values are distinct.
Approach 3: Optimal, compare mid with the right end
Intuition. The compact variant maintains the invariant “the minimum’s index is in [lo, hi]” and needs no candidate variable. Compare nums[mid] with nums[hi]:
nums[mid] > nums[hi]means a larger value precedes a smaller one inside[mid, hi], so the drop, and hence the minimum, lies strictly right ofmid:lo = mid + 1.- Otherwise
nums[mid] < nums[hi], so[mid, hi]is ascending and the minimum is atmidor further left:hi = mid.
When lo == hi the invariant leaves exactly one possible index, and that is the rotation count.
Algorithm.
lo = 0,hi = n - 1.- While
lo < hi:mid = lo + (hi - lo) / 2; ifnums[mid] > nums[hi],lo = mid + 1; elsehi = mid. - Return
lo.
static int optimalCompareRight(int[] nums) {
int lo = 0, hi = nums.length - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] > nums[hi]) lo = mid + 1; // the drop is strictly right of mid
else hi = mid; // nums[mid] < nums[hi]: minimum is mid or left of it
}
return lo;
}
Dry run on nums = [4, 5, 6, 7, 0, 1, 2]:
| lo | hi | mid | nums[mid] | nums[hi] | action |
|---|---|---|---|---|---|
| 0 | 6 | 3 | 7 | 2 | 7 > 2: lo = 4 |
| 4 | 6 | 5 | 1 | 2 | 1 < 2: hi = 5 |
| 4 | 5 | 4 | 0 | 1 | 0 < 1: hi = 4 |
| 4 | 4 | lo == hi: return 4 |
Complexity. Time O(log n), space O(1).
The comparison must be against nums[hi], not nums[lo]. Against the left end, [1, 2, 3, 4, 5] and [3, 4, 5, 1, 2] both give nums[mid] > nums[lo] yet have their minimum on opposite sides. Against the right end the two inputs are told apart correctly, because the minimum is always in the run that ends at hi.
Edge cases and pitfalls
- Not rotated (
k = 0). Approach 2 stops at the first iteration withminIdx = 0; Approach 3 driveshidown to0. - Rotated
n - 1times. The minimum is the last element, e.g.[2, 3, 4, 5, 1]gives4. The right-end comparison movesloall the way ton - 1. - Single element.
k = 0; both searches return0without entering the loop body meaningfully. - Two elements.
[2, 1]gives1,[1, 2]gives0; this checks themid == locase. - Negative values. Nothing changes; only comparisons are used.
[-1, -9, -5, -3]gives1. - Left rotation instead of right. If the problem defines rotation as moving the first element to the end, the answer is
(n - minIdx) % n. Read the statement carefully; the sheet version uses right rotation, where the answer isminIdxitself. - Duplicates. Both binary searches assume distinct values. With duplicates
nums[mid] == nums[hi]is ambiguous, and the rotation count is not even well defined ([1, 1, 1]could have been rotated any number of times). - Overflow in
mid. Uselo + (hi - lo) / 2.
Related problems
- Find minimum in Rotated Sorted Array is the same search returning the value instead of the index.
- Search in rotated sorted array-I uses the rotation point to decide which sorted run to search.
- Search X in sorted array is the plain binary search all of these are built on.