Binary Search using start end vs. using start end
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The two common binary-search styles differ by the interval they maintain. One style uses an inclusive interval, usually written conceptually as start through end, and loops while start <= end. The other uses a half-open interval, conceptually start through end exclusive, and loops while start < end. Neither style is inherently better, but each has its own invariants and update rules.
Inclusive Interval Style
In the inclusive version, both bounds are candidate positions. That means the search interval is conceptually closed on both ends.
A standard implementation:
This style is intuitive for exact-element lookup because the loop condition naturally says, “search while there is still at least one candidate element left.”
Half-Open Interval Style
In the half-open version, end is one past the last candidate position. The active interval is conceptually start inclusive and end exclusive.
Notice the updates:
- move right with
start = mid + 1 - move left with
end = mid
There is no -1 on the left-side update because end is exclusive.
Why the Two Styles Feel Different
The difference is not only the loop condition. It is the interval invariant the code promises to maintain.
Inclusive style invariant:
- candidates live between
startandend, both included
Half-open style invariant:
- candidates live between
startandend, whereenditself is not included
Once you pick one invariant, all updates have to match it. Most binary-search bugs happen when code mixes one loop condition with the other style’s update rules.
Half-Open Style Is Good for Boundary Searches
The half-open interval is often convenient for lower-bound and insertion-point problems.
Example: find the first index where nums[index] >= target.
This style maps naturally to “search in a prefix of the array” problems because start and end behave like slice boundaries.
Inclusive Style Is Good for Straight Membership Tests
If your goal is simply “find the target or report failure,” many developers find the inclusive version easier to reason about.
That is because the interval directly corresponds to actual valid indices, and the termination condition start > end clearly means “the candidate interval is empty.”
It is a strong default when teaching or writing a one-off exact lookup.
Midpoint Formula Matters Too
No matter which style you choose, compute the midpoint like this:
In Python, integer overflow is not a practical issue, but this formula is still the standard habit because it carries over safely to languages with fixed-width integers.
The midpoint rule is independent of the inclusive vs half-open interval choice.
Pick One Invariant and Stay Consistent
A useful rule is:
- inclusive interval: initialize
end = len(nums) - 1, loop with<=, update left side withend = mid - 1 - half-open interval: initialize
end = len(nums), loop with<, update left side withend = mid
Once you mix those rules, off-by-one errors appear quickly.
For example, using while start < end together with end = mid - 1 is often a sign that the code is blending incompatible interval models.
Common Pitfalls
The most common mistake is mixing inclusive and half-open update rules in the same implementation. Another is forgetting whether end currently points to a valid candidate index or to one past the valid range. Developers also often use the exact-lookup pattern when they really need a boundary search such as lower bound or first occurrence. A final issue is changing the loop condition without re-deriving the invariant, which turns a once-correct binary search into a subtle off-by-one bug.
Summary
- '
start <= endusually goes with an inclusive interval.' - '
start < endusually goes with a half-open interval.' - Neither style is universally better; the important part is internal consistency.
- Half-open intervals are especially convenient for boundary and insertion-point searches.
- Most binary-search bugs come from mixing one style’s loop condition with the other style’s updates.

