Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
A sorted array rotated at an unknown pivot still has the property that at any mid, at least one of the two halves [lo, mid] or [mid, hi] is fully sorted. In one binary search, figure out which half is sorted, test whether the target falls inside that sorted half's value range, and discard the half that can't contain it. O(log n) time, O(1) space — no need to find the pivot first.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you can adapt the binary-search invariant to a non-monotonic array by reasoning about which half stays sorted, instead of giving up and scanning linearly.
Where candidates slip
Comparing only nums[mid] to target (plain binary search) and ignoring rotation, or getting the boundary `<=` wrong so a target equal to nums[lo] or nums[hi] is dropped.
How to stand out
Explain *why* one half is always sorted (a single rotation = one discontinuity), then write the four-way branch carefully and dry-run a case where the target is in the unsorted half.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewnums[mid] == target?nums[lo] <= nums[mid]? (left half sorted)YesNoExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Linear Scan (Baseline)
1Hint 1Explore hintHide hint
Forget the rotation entirely — what's the trivial way to find a value?
2Hint 2Explore hintHide hint
Scan every element and compare to the target.
3Hint 3Explore hintHide hint
It's O(n) and ignores both sortedness and rotation, so it's just a baseline.
A · Core idea
Walk the array and return the first index whose value equals the target, else -1.
Why mention it. It's a correctness anchor and a complexity to beat. Why it's wrong here. The prompt requires O(log n); a scan ignores the structure (sorted, single rotation) that lets us halve the search.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
nums = [4, 5, 6, 7, 0, 1, 2], target = 0i=0..3
4,5,6,7 — none equal 0
continue4C · Code with teaching comments
Reference implementation previewpublic int search(int[] nums, int target) { for (int i = 0; i < nums.length; i++) { if (nums[i] == target) { return i; }Explore the full codeHide the full code
D · Why it works and complexityO(n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
Rotation feels like it destroys sortedness, but it only introduces one discontinuity. A linear scan throws away that structure; binary search exploits it.
ComplexityO(n) time, O(1) space — Up to n comparisons, constant memory.
E · Edge cases and pitfalls2Submitting this when O(log n) is required.Explore edge casesHide edge cases
Pitfalls
- Submitting this when O(log n) is required.
- Assuming you must locate the pivot to do better — you don't.
Approach 2 of 2 · Optimal
One-Pass Binary Search — Which Half Is Sorted (Optimal)
1Hint 1Explore hintHide hint
Picture cutting a sorted array once and swapping the pieces. Around any middle index, is it possible for BOTH halves to be broken at the same time?
2Hint 2Explore hintHide hint
Exactly one half [lo..mid] or [mid..hi] is a clean sorted run. Detect it by comparing nums[lo] with nums[mid].
3Hint 3Explore hintHide hint
If the sorted half's value range brackets the target, recurse into it; otherwise recurse into the other half. Keep the inclusive [lo, hi] template.
A · Core idea
Run a standard binary search, but augment the mid step to cope with rotation.
The structural fact. A single rotation of a sorted array creates exactly one point where a larger value is immediately followed by a smaller one. For any mid, the break point lies in at most one of the two halves [lo, mid] and [mid, hi] — so the other half is a contiguous sorted run. That guarantees that at every step at least one half is sorted, and we can reason about it precisely.
Detecting the sorted half. Compare nums[lo] with nums[mid]. If nums[lo] <= nums[mid], the left half [lo, mid] is sorted (no break between them). Otherwise the break is on the left, which means the right half [mid, hi] is the sorted one. The <= matters: when lo == mid (window of size 1 or 2) the left half is trivially 'sorted', and <= keeps that case in the left branch.
Range-testing the target. Once we know which half is sorted, we know its exact value bounds, so we can ask in O(1) whether the target lies inside it. If the left half is sorted and nums[lo] <= target < nums[mid], the target — if present — must be in the left half, so set hi = mid - 1; otherwise it must be in the right half, so lo = mid + 1. Symmetrically, if the right half is sorted and nums[mid] < target <= nums[hi], search right (lo = mid + 1), else search left (hi = mid - 1).
Why the boundary comparisons use the asymmetric `<` / `<=`. We've already returned if nums[mid] == target, so mid itself is excluded. On the sorted left half the inclusive end is nums[lo] (could equal target) and the exclusive end is nums[mid] — hence nums[lo] <= target < nums[mid]. On the sorted right half it mirrors: nums[mid] < target <= nums[hi]. Getting these endpoints right is what separates a correct solution from one that drops targets sitting exactly at lo or hi.
Termination and absence. The window strictly shrinks every iteration (we always move past mid), so the loop ends; if it exits, the target isn't there and we return -1.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
nums = [4, 5, 6, 7, 0, 1, 2], target = 0Array state
lo=0, hi=6
mid=3 (7). Left [4..7] sorted, 0 not inside → go right
4C · Code with teaching comments
D · Why it works and complexityO(log n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
You never need to know where the pivot is — only which side of mid is currently clean. That reframing (local sortedness, not global structure) is the reusable idea: it reappears in Find Minimum in Rotated Sorted Array and in the duplicates variant, where the only change is how you break the nums[lo] == nums[mid] tie.
ComplexityO(log n) time, O(1) space — Each iteration discards half the window with O(1) work, so O(log n) iterations and O(1) extra space.
E · Edge cases and pitfalls8nums = [1], target = 1 — lo=hi=0, mid=0, direct hit → return 0.Explore edge casesHide edge cases
Edge cases handled
nums = [1], target = 1lo=hi=0, mid=0, direct hit → return 0.nums = [1], target = 0mid=0, no match, left half 'sorted', 0 not in [1,1) → lo=1, loop ends → -1.nums = [3, 1], target = 1mid=0 (3). nums[lo]=3 <= 3 left sorted; 1 not in [3,3) → lo=1; then mid=1 → match, return 1.un-rotated nums = [1,2,3,4,5], target = 4Left half always detected sorted → behaves exactly like plain binary search.
Pitfalls
- Using `<` instead of `<=` in `nums[lo] <= nums[mid]` — mishandles the lo == mid window.
- Wrong endpoint inclusivity in the range tests — drops targets equal to nums[lo] or nums[hi].
- Trying to handle duplicates with this exact code — equal nums[lo]/nums[mid] is ambiguous and needs a separate shrink step.
- Range-testing against the *unsorted* half, whose bounds don't bracket its contents.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int search(int[] nums, int target) { int lo = 0, hi = nums.length - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (nums[mid] == target) {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Range-testing against the wrong (unsorted) half
You can only reason about value bounds on the SORTED half. Checking the target against the unsorted half's endpoints gives meaningless comparisons and wrong branches.
Explore the code comparisonHide the code comparison
Range-testing against the wrong (unsorted) half
You can only reason about value bounds on the SORTED half. Checking the target against the unsorted half's endpoints gives meaningless comparisons and wrong branches.
Don't write this
# assumes left is always usable
if nums[lo] <= target <= nums[mid]:
hi = mid - 1
else:
lo = mid + 1Write this instead
if nums[lo] <= nums[mid]: # confirm left sorted first
if nums[lo] <= target < nums[mid]:
hi = mid - 1
else:
lo = mid + 1
else:
if nums[mid] < target <= nums[hi]:
lo = mid + 1
else:
hi = mid - 12Strict vs non-strict boundary
mid is already excluded (we returned on equality), but lo and hi are not. Using `<` on the inclusive end drops a target equal to nums[lo] or nums[hi].
Explore the code comparisonHide the code comparison
Strict vs non-strict boundary
mid is already excluded (we returned on equality), but lo and hi are not. Using `<` on the inclusive end drops a target equal to nums[lo] or nums[hi].
Don't write this
if (nums[lo] < target && target < nums[mid]) { // drops target == nums[lo]
hi = mid - 1;
}Write this instead
if (nums[lo] <= target && target < nums[mid]) {
hi = mid - 1;
}Revise
Carry the pattern into the interview
Pattern to remember
Binary search + 'which half is sorted?'
Code pattern to memorize
while lo <= hi:
mid = lo + (hi - lo) // 2
if nums[mid] == target: return mid
if nums[lo] <= nums[mid]: # left half sorted
if nums[lo] <= target < nums[mid]: hi = mid - 1
else: lo = mid + 1
else: # right half sorted
if nums[mid] < target <= nums[hi]: lo = mid + 1
else: hi = mid - 1
return -1Rules to remember
- 1At every mid, compare
nums[lo]tonums[mid]— ifnums[lo] <= nums[mid]the left half is sorted, otherwise the right half is. - 2Use
<=innums[lo] <= nums[mid]so the single-element case (lo == mid) is treated as a sorted left half. - 3Only commit to a half after checking the target lies within that sorted half's value bounds; otherwise search the other half.
- 4Use the same overflow-safe
mid = lo + (hi - lo) / 2and inclusivewhile (lo <= hi)as plain binary search. - 5You do not need to find the pivot first — one pass handles rotation directly.
Use this pattern when
- A sorted array was rotated by an unknown amount and you need O(log n) lookup
- Any 'piecewise sorted / one discontinuity' array where half is always clean
- Follow-ups that combine rotation with search (find min, find pivot, search with duplicates)
Choose another pattern when
- Array isn't rotated → plain binary search, no half-check needed
- Duplicates are allowed (LeetCode 81) → `nums[lo] == nums[mid]` is ambiguous; shrink lo by one, worst case O(n)
- You need the rotation count itself → that's Find Minimum, a simpler mid-vs-hi compare
Interview takeaway
The whole trick is that rotation creates exactly one 'break point', so one of the two halves around any mid is always a clean sorted range you can range-test against. Recognising 'at least one half is sorted' generalises to every rotated-array variant.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternBinary search on a piecewise-sorted array. Recognise it when a sorted array is rotated/shifted once: at any mid, one half is a clean sorted run you can range-test. Same family as Find Minimum in Rotated Sorted Array and Find Peak Element.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Search in Rotated Sorted Array IIComing soonDuplicates allowed — when nums[lo] == nums[mid] == nums[hi], shrink both ends by one; worst case O(n).
- Find Minimum in Rotated Sorted ArrayJust locate the pivot by comparing nums[mid] to nums[hi] — no target needed.
- Binary SearchThe un-rotated base case — same template without the half-sorted branch.
- Find Peak ElementAnother 'compare mid to a neighbour to pick a half' binary search on an unsorted-but-structured array.