What the rotation point is
A rotated array is a sorted array shifted cyclically: 1 2 3 4 5 becomes 3 4 5 1 2. The rotation point is the index where the order breaks, the one position r where a[r-1] > a[r]. Here it is index 3, holding the minimum. If the array was never rotated there is no break, and the rotation point is 0 by convention. For a right rotation by k, the rotation point is k.
Say that definition before coding. It settles the unrotated case, and it makes the duplicate version testable later. The neighbouring page on searching a rotated sorted array finds a target directly; finding the break first is the reusable primitive behind it.
Why you compare against the right end
The invariant is that the answer lies in [lo, hi]. You need one comparison at mid that tells you which side to keep.
Comparing with the left end does not work. In 1 2 3 4 5 and in 3 4 5 1 2, a[lo] <= a[mid] holds both times. In the first the answer is at lo; in the second it is to the right of mid. One comparison, two different answers.
The right end separates the cases. If a[mid] > a[hi], mid sits in the higher run and hi in the lower run, so the break is in (mid, hi]. If a[mid] < a[hi], everything from mid to hi is in order. The break is then at mid or to its left, and mid itself may be the minimum. That is why hi = mid, not mid - 1.
int rotationPoint(int[] a) {
int lo = 0, hi = a.length - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] > a[hi]) lo = mid + 1;
else hi = mid;
}
return lo;
}
The loop condition and the update must be chosen together. Floor division keeps mid strictly below hi, so hi = mid always shrinks the range. With lo <= hi, the range of one element would loop forever. Each step halves the range, so the binary search runs in O(log n).
Duplicates make the equal case blind
Now allow repeats. In 1 0 1 1 1 and 1 1 1 0 1, with lo = 0 and hi = 4, mid is 2 and a[mid] == a[hi] in both. The break is left of mid in the first and right of it in the second. The comparison has lost its information.
The standard move is hi--. It is safe for the minimum value, because a[mid] still holds that value and stays in range. But it removes one candidate per step, not half. On an array of all ones with a single zero, it steps one index at a time, so the worst case is O(n). The rotated-search page argues why no algorithm beats linear here, so you can cite that rather than rebuild it.
Duplicates return the right value at the wrong index
This is the part most answers miss. Plain hi-- finds the minimum value, but it can return the wrong rotation point. Take 1 1 1 2 1. The break is at index 4, because 2 > 1. The loop sees a[2] == a[4], drops index 4, and ends up returning index 0. a[0] is also a 1, so a test that checks only the value passes. A caller using that index to undo the rotation gets 1 1 1 2 1 back, which is not sorted.
The fix is one check before you discard hi. If a[hi - 1] > a[hi], then hi is the break, so return it.
int rotationPointWithDuplicates(int[] a) {
int lo = , hi = a.length - ;
(lo < hi) {
lo + (hi - lo) / ;
(a[mid] > a[hi]) lo = mid + ;
(a[mid] < a[hi]) hi = mid;
{
(a[hi - ] > a[hi]) hi;
hi--;
}
}
lo;
}
Why is that enough? In a rotated non-decreasing array there is at most one break. hi-- discards only index hi, so the break survives in range unless it was hi, and the check catches exactly that case. If there is no break at all, lo never moves, because it moves only when a[mid] > a[hi], and the answer 0 comes out.
Test it the way the bug hides: brute-force the answer on small random rotated array inputs with values drawn from {0, 1, 2}, and compare indices, not values. Comparing values passes the broken version.
In a rotated array, compare mid with the right end and keep mid in range. With duplicates, accept the linear worst case, and check that the element you are about to drop is not the break itself.
© 2026 Preptima. Originally published at preptima.com (opens in a new tab).