When searching through large sorted datasets like dictionaries, phone directories, or database indexes, checking every element would be impractical. Binary search dramatically reduces search time by eliminating half the remaining elements with each comparison, making it essential for efficient lookups in sorted collections.

Comparison with Linear Search

basic.py
# Basic binary search (iterative)


def binary_search(arr, target):
    """Binary search in sorted array, return index or -1"""
    low = 0
    high = len(arr) - 1

    # Iterative binary search
    while low <= high:
        mid = (low + high) // 2

        if arr[mid] == target:
            return mid  # found
        elif arr[mid] < target:
            low = mid + 1  # search right half
        else:
            high = mid - 1  # search left half

    return -1  # not found


# Test binary search
sorted_list = 

print("List:", sorted_list)
print("Search 7:", binary_search(sorted_list, 7))
print("Search 1:", binary_search(sorted_list, 1))
print("Search 19:", binary_search(sorted_list, 19))
print("Search 10:", binary_search(sorted_list, 10))

# Basic binary search (iterative)


def binary_search(arr, target):
    """Binary search in sorted array, return index or -1"""
    low = 0
    high = len(arr) - 1

    # Iterative binary search
    while low <= high:
        mid = (low + high) // 2

        if arr[mid] == target:
            return mid  # found
        elif arr[mid] < target:
            low = mid + 1  # search right half
        else:
            high = mid - 1  # search left half

    return -1  # not found


# Test binary search
sorted_list = 

print("List:", sorted_list)
print("Search 7:", binary_search(sorted_list, 7))
print("Search 1:", binary_search(sorted_list, 1))
print("Search 19:", binary_search(sorted_list, 19))
print("Search 10:", binary_search(sorted_list, 10))

# Basic binary search (iterative)


def binary_search(arr, target):
    """Binary search in sorted array, return index or -1"""
    low = 0
    high = len(arr) - 1

    # Iterative binary search
    while low <= high:
        mid = (low + high) // 2

        if arr[mid] == target:
            return mid  # found
        elif arr[mid] < target:
            low = mid + 1  # search right half
        else:
            high = mid - 1  # search left half

    return -1  # not found


# Test binary search
sorted_list = 

print("List:", sorted_list)
print("Search 7:", binary_search(sorted_list, 7))
print("Search 1:", binary_search(sorted_list, 1))
print("Search 19:", binary_search(sorted_list, 19))
print("Search 10:", binary_search(sorted_list, 10))

recursive.py
# Recursive binary search


def binary_search_recursive(arr, target, low, high):
    """Recursive binary search implementation"""
    # Base case: search space exhausted
    if low > high:
        return -1  # not found

    # Recursive case
    mid = (low + high) // 2

    if arr[mid] == target:
        return mid  # found
    elif arr[mid] < target:
        return binary_search_recursive(arr, target, mid + 1, high)  # right
    else:
        return binary_search_recursive(arr, target, low, mid - 1)  # left


def binary_search(arr, target):
    """Binary search wrapper"""
    return binary_search_recursive(arr, target, 0, len(arr) - 1)


# Test recursive binary search
sorted_list = [2, 5, 8, 12, 16, 23, 38, 45, 56, 67, 78]

print("List:", sorted_list)
print("Search 23:", binary_search(sorted_list, 23))
print("Search 2:", binary_search(sorted_list, 2))
print("Search 100:", binary_search(sorted_list, 100))

trace.py
# Binary search with trace


def binary_search_trace(arr, target):
    """Binary search with step-by-step trace"""
    low = 0
    high = len(arr) - 1
    iteration = 0

    # Trace each step
    while low <= high:
        iteration += 1
        mid = (low + high) // 2

        print(
            f"Iter {iteration}: low={low}, mid={mid}, high={high}, arr[mid]={arr[mid]}"
        )

        if arr[mid] == target:
            print(f"Found at index {mid}")
            return mid
        elif arr[mid] < target:
            print("  Target > mid, search right")
            low = mid + 1
        else:
            print("  Target < mid, search left")
            high = mid - 1

    print("Not found")
    return -1


# Test with trace
sorted_list = [10, 20, 30, 40, 50, 60, 70, 80, 90]

print("Searching for 70:")
binary_search_trace(sorted_list, 70)

print("\nSearching for 25:")
binary_search_trace(sorted_list, 25)

insertion_point.py
# Find insertion point


def find_insertion_point(arr, value):
    """Find index where value should be inserted to maintain sort order"""
    low = 0
    high = len(arr) - 1

    # Modified binary search for insertion point
    while low <= high:
        mid = (low + high) // 2

        if arr[mid] < value:
            low = mid + 1
        else:
            high = mid - 1

    return low  # insertion point


# Test insertion point finding
sorted_list = [10, 20, 30, 40, 50, 60, 70]

print("List:", sorted_list)
print("Insert 25 at index:", find_insertion_point(sorted_list, 25))
print("Insert 5 at index:", find_insertion_point(sorted_list, 5))
print("Insert 75 at index:", find_insertion_point(sorted_list, 75))
print("Insert 40 at index:", find_insertion_point(sorted_list, 40))

comparison.py
# Compare with linear search


def linear_search(arr, target):
    """Linear search with comparison count"""
    comparisons = 0
    for i in range(len(arr)):
        comparisons += 1
        if arr[i] == target:
            print(f"  Linear: {comparisons} comparisons")
            return i
    print(f"  Linear: {comparisons} comparisons (not found)")
    return -1


def binary_search(arr, target):
    """Binary search with comparison count"""
    low, high = 0, len(arr) - 1
    comparisons = 0

    while low <= high:
        comparisons += 1
        mid = (low + high) // 2

        if arr[mid] == target:
            print(f"  Binary: {comparisons} comparisons")
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1

    print(f"  Binary: {comparisons} comparisons (not found)")
    return -1


# Compare performance
# Create large sorted list
sorted_list = [i * 2 for i in range(1000)]  # even numbers 0, 2, 4, ..., 1998

print(f"List size: {len(sorted_list)}")
print("\nSearch for 998:")
linear_search(sorted_list, 998)
binary_search(sorted_list, 998)

print("\nSearch for 1999 (not found):")
linear_search(sorted_list, 1999)
binary_search(sorted_list, 1999)

| Size | Linear (worst) | Binary (worst) | |------|----------------|----------------| | 10 | 10 | 4 | | 100 | 100 | 7 | | 1000 | 1000 | 10 | | 1M | 1,000,000 | 20 |

Characteristics

  • Time complexity: O(log n) - much faster than linear search
  • Space complexity: O(1) iterative, O(log n) recursive
  • Requires: sorted list
  • Best case: O(1) - target is at middle
  • Worst case: O(log n) - target not found or at edge

When to Use

  • Large sorted lists
  • Multiple searches on same data
  • When speed matters
  • Data does not change often
divide_eliminate Each comparison eliminates half the remaining search space
recursive_binary Express binary search as a recursive function with narrowing bounds
insertion_point Find where an element should be inserted to maintain sorted order
logarithmic_time O(log n) means doubling the data only adds one more step

Exercise: practical.py

Implement a function that uses binary search to find the closest value to a target in a sorted list