Binary search — iterative, recursive; O(log n); searching in sorted - rotated arrays
WHY binary search exists
Think of guessing a number between 1 and 1000. Guess 500: "higher/lower" reply kills 500 numbers. Ten guesses () is enough. That's the whole trick.
WHAT it is
HOW — deriving the loop from first principles
We maintain a window [lo, hi] that is guaranteed to contain the target if it exists at all. This is the invariant — everything else follows from protecting it.
- Start:
lo = 0,hi = n-1(whole array). - Pick
midbetween them. Why mid? It splits the window as evenly as possible, so the worse half is as small as possible → fewest steps in the worst case. - Compare
arr[mid]totarget:- equal → found, return
mid. arr[mid] < target→ target (if present) is to the right, solo = mid + 1. Why+1?miditself was just ruled out; keeping it risks an infinite loop.arr[mid] > target→hi = mid - 1for the same reason.
- equal → found, return
- Stop when
lo > hi(empty window) → not found.
Iterative code
def bsearch(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi: # window non-empty
mid = lo + (hi - lo) // 2 # overflow-safe
if arr[mid] == target:
return mid
elif arr[mid] < target:
lo = mid + 1 # discard left half incl. mid
else:
hi = mid - 1 # discard right half incl. mid
return -1Why lo <= hi not lo < hi? When lo == hi the window still has one element we haven't checked — dropping it would miss single-element matches.
Recursive code
def bsearch_rec(arr, target, lo, hi):
if lo > hi: # base case: empty
return -1
mid = lo + (hi - lo) // 2
if arr[mid] == target:
return mid
if arr[mid] < target:
return bsearch_rec(arr, target, mid + 1, hi) # right
return bsearch_rec(arr, target, lo, mid - 1) # leftSame logic; the call stack replaces the loop. Recursion depth is , so memory is vs for iterative.
WHY it is — derived
Let = comparisons in the worst case for elements. Each step does work then recurses on half: Unrolling: until shrinks to . Number of halvings satisfies . So Space: iterative , recursive (stack).

Searching in a ROTATED sorted array
A sorted array rotated at a pivot, e.g. [4,5,6,7,0,1,2]. It's no longer globally sorted, but at least one half around mid is always sorted. We exploit that.
def search_rotated(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if arr[mid] == target:
return mid
if arr[lo] <= arr[mid]: # left half sorted
if arr[lo] <= target < arr[mid]: # target in left
hi = mid - 1
else:
lo = mid + 1
else: # right half sorted
if arr[mid] < target <= arr[hi]: # target in right
lo = mid + 1
else:
hi = mid - 1
return -1Why <= in arr[lo] <= arr[mid]? When lo == mid (window size 1–2) the "left half" is a single element, trivially sorted; <= keeps that case correct. Still — each step discards a half.
Worked examples
Common mistakes (steel-manned)
Recall Feynman: explain to a 12-year-old
You're looking for a name in a phone book. You don't read every name! You flip to the middle. The name you want comes before the middle? Tear off and ignore the second half. Comes after? Ignore the first half. Open the middle of what's left, repeat. In a few flips you land on the exact page. Each flip throws away half the book — that's why even a giant book takes only a handful of flips. A "rotated" book is one where someone cut the pages and swapped them; but each piece you open is still alphabetical, so you can still tell which side to keep.
Flashcards
Binary search precondition?
Time complexity of binary search and why?
Overflow-safe mid formula?
mid = lo + (hi - lo) // 2, equal to but avoids integer overflow.Why lo = mid + 1 instead of lo = mid?
mid was already compared and excluded; without the window may not shrink → infinite loop.Correct loop condition and why?
while lo <= hi; with < you'd skip the single remaining element when lo == hi.Space complexity: iterative vs recursive?
In a rotated array, how to find the sorted half?
arr[lo] <= arr[mid] the left half is sorted; otherwise the right half is sorted.Once you know the sorted half, how do you decide where to go?
What does binary search return when not found?
lo > hi (empty window).Why is the worst case comparisons?
Connections
- Linear Search — baseline; binary search beats it only when data is sorted.
- Sorting Algorithms — must sort first () to enable binary search.
- Logarithms — the inverse-of-halving that gives the complexity.
- Recurrence Relations — analysis.
- Time Complexity & Big-O — places between and .
- Lower & Upper Bound (bisect) — variants finding first/last position.
- Binary Search Tree — same divide idea as a data structure.
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, binary search ka funda simple hai: array sorted hona chahiye. Phir tum beech wale (middle) element ko dekhte ho. Agar target middle se bada hai to left half pura phenk do, kyunki sorted hai to target wahan ho hi nahi sakta. Agar chhota hai to right half phenk do. Har step me aadha array gayab — isiliye 1000 elements me bhi sirf ~10 comparisons lagte hain. Yahi reason hai ki complexity hai, kyunki har baar half karne ka ulta (inverse) log hota hai.
Do tarike hain likhne ke: iterative (while loop, lo aur hi pointers) aur recursive (function khud ko half pe call karta hai). Iterative me memory , recursive me stack ki wajah se. Mid nikalte waqt hamesha mid = lo + (hi-lo)//2 likho — (lo+hi)//2 overflow de sakta hai bade numbers me. Aur lo = mid + 1 / hi = mid - 1 me mat bhoolna, warna infinite loop me phans jaoge. Loop condition lo <= hi rakho, < nahi — warna last single element miss ho jayega.
Rotated array thoda twist hai, jaise [4,5,6,7,0,1,2] — yeh ghuma hua sorted hai. Trick: mid ke ek taraf hamesha proper sorted rahega. Check karo arr[lo] <= arr[mid] — agar true to left half sorted hai, wahan range-check karke decide karo target idhar hai ya udhar. Phir bhi hi rehta hai. Bas yaad rakho: pehle sorted half pehchano, phir range check.
Yeh topic interviews aur real systems (databases, libraries ka bisect) dono me bahut use hota hai, isliye iska invariant — "window me agar target hai to wahi rahega" — dimaag me clear rakhna.