Binary Search
Algorithm
Programming
Data Structures
Computer Science

How to logically interpret any variation of binary search

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Many binary-search bugs come from memorizing one template without understanding the invariant behind it. Variations such as first occurrence, last occurrence, lower bound, upper bound, and answer-space search all use the same core idea: maintain a valid interval where the solution must exist, then shrink it safely. Once you reason from invariants, any binary-search variation becomes a predictable transformation instead of trial-and-error index tweaking.

This article provides a practical mental model for interpreting and building binary-search variants with fewer off-by-one mistakes.

Core Sections

1. Start from monotonicity

Binary search requires a monotonic condition.

python
# example predicate: is x >= target?
def ok(x, target):
    return x >= target

If predicate transitions once from false to true (or true to false), binary search can locate the boundary.

2. Define interval semantics explicitly

Pick one style and stick to it: closed [lo, hi] or half-open [lo, hi).

python
# closed interval
while lo <= hi:
    mid = lo + (hi - lo) // 2

Half-open intervals often simplify bounds in library-like utilities.

3. Map common variants to boundary goals

  • Find any exact value.
  • Find first index where predicate is true (lower bound).
  • Find last index where predicate is true.
  • Find insertion position for sorted array.
python
1def lower_bound(nums, target):
2    lo, hi = 0, len(nums)
3    while lo < hi:
4        mid = (lo + hi) // 2
5        if nums[mid] < target:
6            lo = mid + 1
7        else:
8            hi = mid
9    return lo

This returns first index >= target.

4. Understand update rules logically

When condition at mid is true, either keep mid in search space or drop it depending on desired boundary.

python
1# first true boundary
2if condition(mid):
3    hi = mid
4else:
5    lo = mid + 1

This pattern is the backbone for many interview and production variations.

5. Binary search on answer space

You can search over values, not only array indices.

python
1def min_speed(piles, hours):
2    lo, hi = 1, max(piles)
3    while lo < hi:
4        mid = (lo + hi) // 2
5        needed = sum((p + mid - 1) // mid for p in piles)
6        if needed <= hours:
7            hi = mid
8        else:
9            lo = mid + 1
10    return lo

Here the monotonic predicate is "can finish within hours at speed k".

6. Prove correctness with invariants

Before coding, state invariant in words. Example for lower bound: "all indices < lo are invalid, all indices >= hi are valid." Each update must preserve this.

text
invariant:
- left side known false
- right side known true

Writing this once prevents most boundary bugs.

Common Pitfalls

  • Using binary search on non-monotonic predicates.
  • Mixing closed and half-open interval logic in the same loop.
  • Updating lo/hi without preserving invariant guarantees.
  • Returning mid after loop when boundary index should be returned.
  • Forgetting overflow-safe midpoint formula in fixed-width integer languages.

Summary

Any binary-search variation can be interpreted as boundary discovery on a monotonic condition. Choose interval semantics, define invariant, and derive updates from that invariant instead of memorizing index recipes. This approach scales from basic array lookup to advanced answer-space optimization problems. With invariant-first reasoning, binary search becomes one reusable idea rather than many fragile templates.

For teams maintaining how to logically interpret any variation of binary search in long-lived codebases, reliability improves when implementation guidance is paired with a lightweight verification routine. A practical pattern is to define three test categories up front. First, happy-path tests that validate normal expected inputs. Second, boundary tests that include empty values, minimum and maximum limits, and malformed records from real logs. Third, operational tests that simulate production-like behavior under retries, parallel execution, and partial failure. This combination catches both obvious logic defects and the subtle integration issues that usually appear after deployment.

It is also useful to encode assumptions close to the code rather than leaving them in scattered documentation. Add short comments where invariants matter, keep helper utilities centralized, and avoid repeating slightly different logic in multiple modules. In CI, run a small deterministic suite on every commit and a broader dataset suite on schedule. When incidents occur, convert the failing scenario into a permanent regression test before patching. Over time this creates a strong feedback loop where how to logically interpret any variation of binary search behavior remains stable even as dependencies, framework versions, and team ownership change. The result is less firefighting and faster review cycles.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.