Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Search a sorted array for a target in O(log n). Maintain an inclusive window [lo, hi]; each step compute mid = lo + (hi - lo) / 2, compare nums[mid] to target, and discard the half that cannot contain it. The two things that break candidates are integer overflow on (lo + hi) and off-by-one bugs in the loop condition — use `while (lo <= hi)` with `hi = mid - 1` / `lo = mid + 1`.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you can implement binary search bug-free under pressure — correct loop bounds, overflow-safe mid, and a termination guarantee — not just recite the idea.
Where candidates slip
Writing `mid = (lo + hi) / 2` (overflow), using `while (lo < hi)` with an inclusive window (skips the last element), or updating `lo = mid` instead of `mid + 1` (infinite loop).
How to stand out
State your invariant out loud ('the answer, if present, is always inside [lo, hi]'), justify the `<=`, and mention the overflow-safe mid formula before the interviewer has to ask.
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 previewFind target in an arrayIs the array sorted?NoYesExplore 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
Ignoring the 'sorted' property, how would you find a value with no cleverness at all?
2Hint 2Explore hintHide hint
Walk every element left to right and compare it to the target.
3Hint 3Explore hintHide hint
Return the index on the first match, -1 if you reach the end. It works but it ignores the one fact (sortedness) that lets us go faster.
A · Core idea
Walk the array from index 0 and compare each element to target, returning the index on the first equal element and -1 if the loop completes.
Why mention it. It establishes a correct baseline and a complexity to beat. Stating 'the naive scan is O(n)' signals that you understand the cost you're about to improve on.
Why it's wrong for this problem. The prompt explicitly demands O(log n). A linear scan throws away the most valuable piece of information we were handed — that the array is sorted. Sortedness means one comparison can eliminate half the array, not just one element.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
nums = [-1, 0, 3, 5, 9, 12], target = 9i=0
-1 == 9? no
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
The linear scan extracts exactly one bit of information per comparison ('is this the answer?'). Binary search extracts a far more valuable bit ('is the answer left or right of here?'), which is why it collapses n into log n.
ComplexityO(n) time, O(1) space — One pass over up to n elements, a single comparison each, no extra memory.
E · Edge cases and pitfalls2Submitting this when the prompt demands O(log n) — it passes small tests but signals you missed the point.Explore edge casesHide edge cases
Pitfalls
- Submitting this when the prompt demands O(log n) — it passes small tests but signals you missed the point.
- Forgetting the trailing return -1 when no element matches.
Approach 2 of 2 · Optimal
Binary Search — Inclusive Bounds (Optimal)
1Hint 1Explore hintHide hint
The array is sorted. If you look at the middle element and it's smaller than the target, what does that tell you about everything to its left?
2Hint 2Explore hintHide hint
Keep a window [lo, hi]. Look at mid; if nums[mid] < target the answer must be to the right, so move lo past mid; if it's larger, move hi before mid.
3Hint 3Explore hintHide hint
Loop while lo <= hi. Compute mid as lo + (hi - lo) / 2 to avoid overflow, and always set lo = mid + 1 or hi = mid - 1 so the window strictly shrinks.
A · Core idea
Maintain an inclusive window [lo, hi] that always contains the answer if it exists. Initialise lo = 0, hi = n - 1. While lo <= hi, look at mid = lo + (hi - lo) / 2. If nums[mid] == target, return mid. If nums[mid] < target, the target (being larger) must lie strictly to the right, so set lo = mid + 1. Otherwise it lies strictly to the left, so set hi = mid - 1. If the loop exits, the window is empty and the target is absent — return -1.
The invariant that makes it correct. At every step, if the target is in the array, it is somewhere in [lo, hi]. Each comparison lets us discard the half that provably cannot contain it: because the array is sorted, nums[mid] < target means every index ≤ mid is also < target, so none of them can be the answer. Discarding half each time gives the logarithmic bound.
Why `lo <= hi` and not `lo < hi`. With inclusive bounds, the window [lo, hi] is non-empty exactly when lo <= hi. When lo == hi there is still one element left to check — the target could be it. Using < would skip that final element and miss answers (e.g. a single-element array, or the target sitting at the last position the search narrows to).
Why `mid = lo + (hi - lo) / 2` instead of `(lo + hi) / 2`. Both compute the same midpoint mathematically, but lo + hi can exceed Integer.MAX_VALUE in Java when both indices are large, wrapping to a negative number and throwing ArrayIndexOutOfBoundsException. lo + (hi - lo) / 2 never forms the large intermediate sum, so it's overflow-safe. It's a free habit that prevents a notorious bug — famously latent in the JDK's own binarySearch for nearly a decade.
Why `mid + 1` / `mid - 1` and not `mid`. We've already tested mid and it wasn't the answer, so excluding it is correct and guarantees the window shrinks by at least one every iteration. Setting lo = mid when lo == mid would leave the window unchanged and loop forever.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
nums = [-1, 0, 3, 5, 9, 12], target = 9Array state
lo=0, hi=5
mid=2, nums[2]=3 < 9 → discard left half
4C · Code with teaching comments
D · Why it works and complexityO(log n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
Binary search is less about arrays and more about predicates: you're locating the boundary of a monotone condition. Once you internalise the inclusive-bounds template (while lo <= hi, move past mid, return -1/lo at the end), every variant — rotated arrays, 2D matrices, 'smallest speed that works' — is the same skeleton with a different comparison in the middle.
ComplexityO(log n) time, O(1) space — Each iteration halves the window size, so the number of iterations is ⌈log₂(n+1)⌉ = O(log n). Only a few scalar variables are used → O(1) space.
E · Edge cases and pitfalls8nums = [5], target = 5 — lo=hi=0, mid=0, nums[0]==5 → return 0. The `<=` is what lets the single-element window be checked.Explore edge casesHide edge cases
Edge cases handled
nums = [5], target = 5lo=hi=0, mid=0, nums[0]==5 → return 0. The `<=` is what lets the single-element window be checked.nums = [5], target = 2mid=0, 5 > 2 → hi=-1, loop ends, return -1.nums = [2, 5], target = 5lo=0,hi=1,mid=0 → 2<5 → lo=1; then lo=hi=1,mid=1 → match, return 1.target smaller than all / larger than allWindow collapses to one side and exits with lo>hi → return -1, no out-of-bounds access.
Pitfalls
- Using `mid = (lo + hi) / 2` — overflows int in Java for large indices.
- Using `while (lo < hi)` with inclusive bounds — skips the last element.
- Updating `lo = mid` / `hi = mid` — can spin forever when lo == mid.
- Returning `lo` thinking it's the index when not found (that's the insertion point, not a match).
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.
1Overflow in the midpoint
In Java, `lo + hi` is computed as a 32-bit int. For large arrays lo + hi can exceed 2^31 − 1, wrap negative, and crash with ArrayIndexOutOfBoundsException. This bug lived in the JDK for years.
Explore the code comparisonHide the code comparison
Overflow in the midpoint
In Java, `lo + hi` is computed as a 32-bit int. For large arrays lo + hi can exceed 2^31 − 1, wrap negative, and crash with ArrayIndexOutOfBoundsException. This bug lived in the JDK for years.
Don't write this
int mid = (lo + hi) / 2;Write this instead
int mid = lo + (hi - lo) / 2;2Wrong loop condition for inclusive bounds
With hi = nums.length - 1 (inclusive), the window is non-empty when lo == hi. Using `<` exits one step early and misses a target sitting at that last position.
Explore the code comparisonHide the code comparison
Wrong loop condition for inclusive bounds
With hi = nums.length - 1 (inclusive), the window is non-empty when lo == hi. Using `<` exits one step early and misses a target sitting at that last position.
Don't write this
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;Write this instead
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;3Not moving past mid (infinite loop)
When lo == hi == mid, setting lo = mid leaves the window unchanged, so the while loop never terminates.
Explore the code comparisonHide the code comparison
Not moving past mid (infinite loop)
When lo == hi == mid, setting lo = mid leaves the window unchanged, so the while loop never terminates.
Don't write this
if nums[mid] < target:
lo = mid
else:
hi = midWrite this instead
if nums[mid] < target:
lo = mid + 1
else:
hi = mid - 1Revise
Carry the pattern into the interview
Pattern to remember
Halve the search space on sorted data
Code pattern to memorize
lo, hi = 0, n - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if nums[mid] == target: return mid
elif nums[mid] < target: lo = mid + 1
else: hi = mid - 1
return -1Rules to remember
- 1Use
mid = lo + (hi - lo) / 2, never(lo + hi) / 2— the latter overflowsintin Java whenlo + hi > 2^31 − 1. - 2With an inclusive
[lo, hi]window, the loop iswhile (lo <= hi)— the=is required so a single-element window is still inspected. - 3Always move past
mid:lo = mid + 1orhi = mid - 1. Settinglo = midorhi = midcan loop forever. - 4Return
-1(or the insertion point) after the loop — falling off the end means the target is absent. - 5Pick one convention (inclusive
[lo, hi]) and use it everywhere; mixing inclusive and exclusive bounds is the #1 source of binary-search bugs.
Use this pattern when
- Array (or answer space) is **sorted / monotonic** and you need a value or boundary
- Lookups must be faster than O(n) — repeated queries on static sorted data
- Finding a threshold: first/last index satisfying a predicate
- The phrase 'minimize/maximize X such that a feasibility check passes' → binary search on the answer
Choose another pattern when
- Data is unsorted and you only search once → a linear scan or hash set is simpler and O(n)
- Data changes between queries and you'd have to re-sort each time → consider a balanced BST / heap
- You need *all* matches, not a single index → linear scan or expand around the found index
Interview takeaway
Binary search is the canonical 'halve the search space' move. Master the inclusive-bounds template once and you can retarget it to rotated arrays, 2D matrices, and 'binary search on the answer' problems like Koko Eating Bananas — same skeleton, different comparison.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternThe canonical Binary Search template. Recognise it whenever data is sorted or a predicate is monotonic and you can discard half the search space per comparison. The exact skeleton powers Search in Rotated Sorted Array, Search a 2D Matrix, Find Minimum in Rotated Sorted Array, and 'binary search on the answer' problems like Koko Eating Bananas.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Search Insert PositionSame template, but return `lo` after the loop — that's where the target would be inserted.
- Search in Rotated Sorted ArrayArray is rotated — decide which half is sorted, then binary search the half that could contain the target.
- Koko Eating BananasBinary search on the *answer* (eating speed) rather than on array indices.
- Find First and Last PositionComing soonTwo boundary searches — one biased left, one biased right — to handle duplicates.