A rotated sorted array, such as [4, 5, 6, 7, 0, 1, 2], is a sorted array cut at some point and swapped around. It isn't sorted overall, but binary search still works. Split it at mid, and at least one half is always sorted. Find that half, decide whether the target can be in it, and drop the other side.
The idea#
The first job each step is to find the sorted half. Compare nums[mid] with nums[r]:
- If
nums[mid] < nums[r], the right halfmid..ris increasing (the rotation point isn't in it). - Otherwise, the left half
l..midis increasing.
Once you know which half is sorted, you know its range from its two ends. If the target lies between its ends, search there; if not, it must be in the other half. Every step still halves the range, so the search is .
Some problems ask for the minimum instead of a target, and that's a different question. It's a tipping-point problem: the same mid versus r comparison tells you which side of mid holds the drop. See Tipping Point & Peak.
How to spot it#
These cues point to the rotated-array pattern.
- "Rotated sorted array", "rotated at an unknown pivot".
- Find the minimum, the rotation count, or a target in such an array.
- You need on an array that is sorted except for one drop.
Template#
Search for a target. Each branch first finds the sorted half, then checks whether the target is inside it.
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target: return mid
if right half mid..r is sorted: # nums[mid] < nums[r]
if target is inside its range: go right
else: go left
else: # left half l..mid is sorted
if target is inside its range: go left
else: go right
return -1
To see how the sorted half changes from step to step, here is the trace for target 0 in [4, 5, 6, 7, 0, 1, 2].
l |
r |
mid |
Sorted half | Target 0 inside? | Move |
|---|---|---|---|---|---|
| 0 | 6 | 3 (7) | left [4..7] |
✗ | l = 4 |
| 4 | 6 | 5 (1) | right [1..2] |
✗ | r = 4 |
| 4 | 4 | 4 (0) | found | return 4 |
Pitfalls#
The loop shape is standard, so most bugs hide in the comparisons.
- Comparing with the wrong end. Comparing
midwithrkeeps the cases clean. When comparing withl, the "no rotation" case needs extra care. - Strict vs inclusive checks.
nums[mid]was already tested, so usenums[mid] < target <= nums[r]andnums[l] <= target < nums[mid]. - Expecting with duplicates. A tie between
midand an end tells you nothing, so the worst case can be . Say so in an interview. - Dropping a possible answer. When
miditself may be the answer, keep it in the range (r = midwithl < r).
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below.
- 33. Search in Rotated Sorted Array
- 81. Search in Rotated Sorted Array II
- 153. Find Minimum in Rotated Sorted Array
- 154. Find Minimum in Rotated Sorted Array II
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Search in Rotated Sorted Array
This is the template. Use the strict and inclusive checks from the pitfalls so nums[mid] is never counted twice.
Hint: Search in Rotated Sorted Array II
With duplicates, nums[mid] == nums[r] tells you nothing: either half could hold the drop (think [1, 1, 1, 3, 1]). The safe move is r -= 1. It removes one copy of a value still present at mid, so nothing is lost, but the worst case becomes .
Hint: Find Minimum in Rotated Sorted Array
Use l < r. If nums[mid] < nums[r], the minimum is at mid or to its left, so set r = mid. Otherwise the drop is right of mid, so set l = mid + 1. Don't use r = mid - 1: mid may be the minimum.
Hint: Find Minimum in Rotated Sorted Array II
Use the same loop, plus the duplicate rule: if nums[mid] == nums[r], do r -= 1. The worst case is again .
Key takeaways#
- Split at
midand one half is always sorted. - Compare
nums[mid]withnums[r]to tell which half that is. - Search the sorted half if the target fits its range; otherwise go to the other half.
- Finding the minimum is a tipping point:
r = midorl = mid + 1. - Duplicates force
r -= 1on ties, making the worst case .