Binary Search
Debarka Sengupta
Binary Search is an efficient algorithm for finding an item from a sorted list of items. It works by
repeatedly dividing in half the portion of the list that could contain the item until you've
narrowed down the possible locations to just one.
Prerequisite: The array/list must be sorted.
1. Iterative Approach (Using Loops)
The iterative approach uses a while loop to shift boundaries ( low and high ) until the target
is found or the search space is exhausted.
The Algorithm
1. Initialize low = 0 and high = length - 1 .
2. While low <= high :
Calculate mid = (low + high) // 2 .
If array[mid] == target , return mid .
If array[mid] < target , discard the left half: low = mid + 1 .
If array[mid] > target , discard the right half: high = mid - 1 .
3. If the loop ends, the target is not in the array.
Iterative Implementation
def binary_search_iterative(arr, target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
# Check if target is present at mid
if arr[mid] == target:
return mid
# If target is greater, ignore left half
elif arr[mid] < target:
low = mid + 1
# If target is smaller, ignore right half
else:
high = mid - 1
return -1 # Target not found
Dry Run (Iterative)
Input: arr = [2, 5, 8, 12, 16, 23, 38] , target = 23
Step low high mid arr Comparison Action
mid
1 0 6 3 12 12 < 23 low = 3 + 1 = 4
2 4 6 5 23 23 == 23 Found! Return index 5
2. Recursive Approach
The recursive approach follows the "Divide and Conquer" strategy. It calls itself with updated
boundaries as arguments.
The Algorithm
1. Base Case 1: If low > high , the target is not present. Return -1.
2. Calculate mid = (low + high) // 2 .
3. Base Case 2: If array[mid] == target , return mid .
4. Recursive Step:
If array[mid] < target , return binary_search(arr, target, mid + 1, high) .
If array[mid] > target , return binary_search(arr, target, low, mid - 1) .
Recursive Implementation
def binary_search_recursive(arr, target, low, high):
# Base Case: Search space exhausted
if low > high:
return -1
mid = (low + high) // 2
# Base Case: Found the element
if arr[mid] == target:
return mid
# Recursive calls
if arr[mid] < target:
return binary_search_recursive(arr, target, mid + 1, high)
else:
return binary_search_recursive(arr, target, low, mid - 1)
Dry Run (Recursive)
Input: arr = [10, 20, 30, 40, 50] , target = 10
1. Call 1: low=0, high=4 . mid=2 . arr[2]=30 .
30 > 10, so call search(arr, 10, 0, 1) .
2. Call 2: low=0, high=1 . mid=0 . arr[0]=10 .
10 == 10. Found! Return index 0.
3. Asymptotic Complexity Analysis
Time Complexity
The time complexity is derived from the Recurrence Relation for the recursive version (which
applies conceptually to the iterative version as well):
T (n) = T (n/2) + Θ(1)
By applying the Master Theorem (Case 2) where a = 1, b = 2, f (n) = 1: We find that
T (n) = Θ(log n).
Case Notation Description
Best Case Ω(1) Target is the first mid element. Lower bound is constant.
Worst Case O(log n) Target is at the end of the tree or absent. Upper bound is logarithmic.
Average Case Θ(log n) Tight bound for the general case.
Space Complexity
Method Notation Reason
Iterative Θ(1) Uses only a constant number of pointers regardless of n.
Recursive Θ(log n) Each recursion level consumes a stack frame. The depth of the tree is log n.
Summary
Search Speed: Binary search is significantly faster than linear search (Θ(n)) for large
datasets.