Searching & Sorting · Searching & Sorting Fundamentals

Binary Search Basics

Halving the search space on a sorted array instead of scanning linearly.

  • 6 min read
  • 17 practice questions
  • 4 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: the first position that qualifies
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Finding a value in a list of n items means looking at up to n of them, unless the list is sorted. Then one comparison with the middle element tells you which half can't contain the value, and halving 30 times narrows a billion items to one. The idea is simple. The details are where most binary searches go wrong: the loop condition, the updates, duplicates and overflow. This skill makes those details automatic, and covers the sorting that binary search depends on.

We'll go from simple to more technical:

  1. The pattern: halve the range, with the loop condition matched to the updates.
  2. A second flavor: lower bound, the first position that qualifies.
  3. Where binary search runs in production (indexes, version bisecting, lookups).
  4. What breaks it: unsorted input, mismatched loops, duplicates, overflow.

The approach

Java

int search(int[] nums, int target) {
    int lo = 0, hi = nums.length - 1;              // the target, if present, is in [lo, hi]
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;              // never (lo + hi) / 2: the sum can overflow
        if (nums[mid] == target) return mid;
        if (nums[mid] < target) lo = mid + 1;      // mid and everything left of it are too small
        else hi = mid - 1;                         // mid and everything right of it are too big
    }
    return -1;
}

Python

def search(nums: list[int], target: int) -> int:
    lo, hi = 0, len(nums) - 1                      # the target, if present, is in [lo, hi]
    while lo <= hi:
        mid = (lo + hi) // 2                       # Python ints can't overflow; // keeps it an int
        if nums[mid] == target:
            return mid
        if nums[mid] < target:
            lo = mid + 1                           # mid and everything left of it are too small
        else:
            hi = mid - 1                           # mid and everything right of it are too big
    return -1

The loop keeps one promise: if the target exists, it's inside [lo, hi]. Every update removes only positions proven wrong, and mid itself is always removed, so the range shrinks every step and the loop ends. It's O(log n) - about 30 steps for a billion items - but only because the array is sorted. On unsorted data nothing fails loudly: the comparison discards the wrong half and the search quietly reports "not found".

A second flavor: the first position that qualifies

With duplicates, "find the target" has several right answers, and questions usually want a specific one - the first occurrence, the last, or where to insert. Lower bound finds the first index whose value is >= x, using a half-open range whose meaning is "the answer is in [lo, hi]", where hi = n means "nowhere":

Java

int lowerBound(int[] nums, int x) {
    int lo = 0, hi = nums.length;
    while (lo < hi) {                              // lo == hi: the range has one candidate left
        int mid = lo + (hi - lo) / 2;
        if (nums[mid] < x) lo = mid + 1;           // mid is too small - the answer is after it
        else hi = mid;                             // mid might be the answer - keep it
    }
    return lo;
}
// first occurrence: lowerBound(x) (check nums[it] == x); last occurrence: lowerBound(x + 1) - 1

Python

def lower_bound(nums: list[int], x: int) -> int:
    lo, hi = 0, len(nums)
    while lo < hi:                                 # lo == hi: the range has one candidate left
        mid = (lo + hi) // 2
        if nums[mid] < x:
            lo = mid + 1                           # mid is too small - the answer is after it
        else:
            hi = mid                               # mid might be the answer - keep it
    return lo

# the standard library has both bounds: bisect.bisect_left(nums, x) (first >= x), bisect.bisect_right(nums, x) (first > x)

The loop condition must match the updates: with hi = mid (mid kept), the loop has to be lo < hi; with lo <= hi it can sit on lo == hi == mid forever. The same "find where a condition flips" shape handles rotated arrays (one half is always sorted - check which, then whether the target lies in it) and index properties (in a sorted array of pairs with one single element, pairs before the single start at even indices).

Where you'll see it

Database indexes (B-trees) and sorted files use the same halving at every node or block; git bisect binary-searches commit history for the change that introduced a bug; time-series stores find a time range with lower and upper bounds; standard libraries expose it directly (Arrays.binarySearch, Collections.binarySearch, C++'s lower_bound, Python's bisect.bisect_left and bisect_right).

Which tool for which job?

You needUse
Is x present?classic binary search, lo <= hi with mid ± 1
First position ≥ x (first copy, insert point)lower bound, lo < hi with hi = mid
First position > x (just past the last copy)upper bound
Pythonbisect_left / bisect_right (key= since 3.10)
Target likely near the front, or size unknowngalloping (exponential) search
Uniformly spread numeric keysinterpolation search, about log log n probes
A real-valued answera fixed number of iterations, not a tolerance

Common mistakes

MistakeWhat happensDo this instead
Searching unsorted inputsilent wrong answerssort first, or don't binary search
lo <= hi with hi = midan infinite loop at lo == hipair lo < hi with hi = mid
Taking "found" as "first"[2, 2, 2, 2, 2] returns index 2lower bound
(lo + hi) / 2 in Java-397483648 for 1.5e9 and 2e9lo + (hi - lo) / 2 or >>> 1
A tolerance below the doubles' spacingthe loop never endsa fixed iteration count

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. What happens if binary search runs on an unsorted array?

Show answer

It still runs and returns something - but when the middle comparison discards "the wrong half", the target can be thrown away, and the result is a silent "not found" or a wrong index. Sortedness is a precondition the search can't check for itself.

2. A lower-bound loop uses while (lo <= hi) with hi = mid. What goes wrong?

Show answer

When lo == hi, mid == lo; if the condition sends it to hi = mid, nothing changes and the loop never ends. With hi = mid (mid kept as a candidate) the loop must be while (lo < hi), stopping when one candidate remains.

3. How do you get the first and last index of a target in a sorted array with duplicates?

Show answer

First = lowerBound(target) - the first index with a value >= target (if that value equals the target). Last = lowerBound(target + 1) - 1, one before the first value > target. Both are O(log n); plain binary search would return an arbitrary copy.

Key takeaways

  • Binary search keeps one promise - "if it's there, it's in [lo, hi]" - and removes only positions proven wrong.
  • It needs sorted data (or any monotonic condition); on unsorted data it fails silently.
  • Match the loop condition to the updates: lo <= hi with mid ± 1, lo < hi with hi = mid.
  • Lower bound (first >= x) and upper bound (first > x) handle duplicates, first/last occurrence and insertion points.
  • Use lo + (hi - lo) / 2 for the midpoint.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Present?lo <= hi, mid ± 1
First ≥ xlower bound (lo < hi, hi = mid)
First > xupper bound
Answer near the front / unknown sizegalloping
Uniform numeric keysinterpolation (with a fallback)
Real-valued answerfixed iteration count

Snippets worth knowing by heart

Java

int lo = 0, hi = n;                                   // lower bound: first index with a[i] >= x
while (lo < hi) {
    int mid = lo + (hi - lo) / 2;                     // no overflow
    if (a[mid] < x) lo = mid + 1; else hi = mid;
}
return lo;

Python

lo, hi = low_value, high_value
for _ in range(100):                     # real-valued bisection: fixed iterations
    mid = (lo + hi) / 2
    if f(mid) < target:
        lo = mid
    else:
        hi = mid

The traps

  • Unsorted input: binary search returns wrong answers with no error. Sort first, or search linearly.
  • Mismatched loop and updates: lo <= hi with hi = mid loops forever. Pair lo < hi with hi = mid.
  • "Found" isn't "first": in [2, 2, 2, 2, 2] a plain search returns index 2. Use a lower-bound search.
  • Midpoint overflow: (lo + hi) / 2 goes negative for large ints in Java. Use lo + (hi - lo) / 2.
  • Tolerances below one ulp: doubles can't get that close, so the loop never ends. Run a fixed number of iterations.

Staff-level points, one line each

  • (1.5e9 + 2e9) / 2 as int is -397483648; lo + (hi - lo) / 2 gives 1750000000.
  • A 1e-12 tolerance near 1e9 hit a 10,000-iteration cap, since the ulp is 1.19e-7. Use 100 fixed iterations.
  • Galloping: 9 probes against 24 for a target at index 20 of 10 million.
  • Interpolation: 4.2 probes on uniform data, but 31 for 1,060 skewed values.
  • Quicksort with a first-element pivot made 1,999,000 comparisons on sorted input of 2,000 items.

Say it in an interview

  • "I keep an invariant on [lo, hi) and match the loop to the updates."
  • "For duplicates I use lower and upper bounds, not plain 'found'."
  • "The midpoint is lo + (hi - lo) / 2."
  • "If the answer is likely near the front, I'd gallop."

Practice

This skill comes with 17 multiple-choice questions that tell you which misconception a wrong answer reveals and 4 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Binary Search Basics free

Search in a Rotated Sorted Array

MediumProCoding problemJava · Python

Take a sorted list of distinct numbers, cut it in two somewhere and swap the halves: [3, 8, 12, 20, 25] might become [20, 25, 3, 8, 12]. Given such a rotated list nums and a target, return the position of target, or -1 if it is missing.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Single Element in a Sorted Array

MediumProCoding problemJava · Python

In the sorted list nums, every value appears twice in a row except one, which appears just once. Find that value.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

First and Last Position of a Target

MediumProCoding problemJava · Python

nums is sorted and may contain repeats. Return the first and the last position where target appears, or -1 -1 if it doesn't appear at all. Aim for O(log n).

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Binary Search Basics free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime