Binary Search β€” `<=` vs `<` Boundary Conditions

Last modified: July 21, 2026

Binary Search β€” <= vs < Boundary Conditions

The golden rule: Ask β€” “Has this index been fully eliminated as a candidate?”

  • Yes β†’ skip it: use mid + 1 / mid - 1
  • No β†’ keep it: use mid

Every binary search loop falls into one of two mental models. The choice of <= vs < follows directly from which model you pick.

Style A β€” Explicit check, shrink past mid

while (L <= R) {                // L and R are both live candidates
    int mid = L + (R - L) / 2;
    if (arr[mid] == target) return mid;   // checked explicitly
    if (arr[mid] < target)  L = mid + 1; // mid ruled out β†’ skip it
    else                    R = mid - 1; // mid ruled out β†’ skip it
}
// loop ends when search space is empty (L > R)

Because mid is tested in the if, it is definitively ruled out. So mid Β± 1 is safe β€” you won’t skip the answer.

Style B β€” Answer lives at boundary, keep mid

while (L + 1 < R) {              // answer is always inside [L, R]
    int mid = L + (R - L) / 2;
    if (arr[mid] <= target) L = mid; // mid not ruled out β†’ keep it
    else                    R = mid; // mid not ruled out β†’ keep it
}
// loop ends at L + 1 == R β†’ check both
if (arr[L] == target) return L;
if (arr[R] == target) return R;

mid is never checked directly inside the loop, so you cannot skip it. The loop guarantees that exactly two candidates remain at termination.


2. What Each Pointer Means (The Invariant)

Understanding the invariant of each pointer is more reliable than thinking about edge cases one by one.

PointerInvariant in Style B
LLast index that is definitely ≀ target β€” still a candidate because we haven’t verified it equals the target
RFirst index that is definitely > target β€” still a candidate, it could be the answer

Once you know what each pointer guarantees, the boundary checks write themselves.


3. Condition Boundaries β€” <= vs < Inside the Loop

The same rule applies when choosing whether to include endpoints in range checks.

SituationOperatorReason
target could equal nums[L]>= (include)nums[L] is still a candidate β€” not yet ruled out
target vs nums[mid] (already checked above)< (exclude)nums[mid] was tested on the line above β€” definitively ruled out
target could equal nums[R]<= (include)nums[R] is still a candidate

Example β€” Rotated Sorted Array

if (nums[L] <= nums[mid]) {
    // left half is sorted
    if (target >= nums[L] && target < nums[mid])
    //          ^^                    ^
    //  nums[L] is live          nums[mid] already checked
    //  β†’ include with >=        β†’ exclude with <
        R = mid - 1;
    else
        L = mid + 1;
}

4. Common Mistakes Decoded

MistakeWhy it breaks
while (L < R) with R = mid - 1Skips mid before checking it β€” can miss the answer when L == R is the target
while (L <= R) with R = midIf arr[mid] keeps being chosen as R, L never catches up β€” infinite loop
Range check uses < on both boundariesMisses the case where target equals the left boundary exactly
Range check uses <= on midRe-examines a mid that was already tested β€” can push into the wrong half

5. Quick Decision Checklist

Before writing any boundary operator, ask these questions in order:

  1. Has this index already been explicitly checked?

    • Yes (e.g. nums[mid] == target was already tested) β†’ use < / mid Β± 1
    • No β†’ use <= / mid
  2. Can the target equal this boundary value?

    • Yes β†’ use >= or <= to include it
    • No β†’ use > or < to exclude it
  3. Which loop style am I using?

    • Style A (while L <= R) β†’ always use mid Β± 1
    • Style B (while L + 1 < R) β†’ always use plain mid

One-line test: “Has this index already been definitively eliminated?” β†’ Yes: < or mid Β± 1. No: <= or mid.