SECTION 1
Historical Context & Motivation
Imagine you need to look up a word in a dictionary. You wouldn't start on page one and read every single entry — you'd flip to roughly the middle, see if you've gone too far or not far enough, and then repeat. This intuitive strategy is exactly the idea behind binary search, one of the most fundamental algorithms in computer science. The concept has been used by humans for centuries, long before electronic computers existed, whenever people needed to locate information in an ordered collection.
~200 BCE • Earliest Divide-and-Conquer Ideas
1946 • First Published Binary Search
1960 • First Bug-Free Implementation
2001 • Python's bisect Module Released
The core question binary search answers is simple yet powerful: how can we find a target value in a sorted list as quickly as possible? A naïve approach — checking every element one by one — works, but it becomes painfully slow as the list grows. Binary search solves this by eliminating half the remaining possibilities with each comparison, turning a potentially enormous task into a manageable one.
SECTION 2
Core Principles & Definitions
Binary search relies on a few key ideas that work together. Before you can use it, you need to understand each one clearly. The algorithm's elegance comes from combining these principles into a simple loop that rapidly zeroes in on the answer.
Sorted Input Required
Divide and Conquer
Logarithmic Time
Two Pointers: low and high
low and high — that define the current search range. The midpoint is calculated as (low + high) // 2.The bisect Module
bisect_left() and insort() so you don't have to code binary search yourself.KEY TAKEAWAY
SECTION 3
Visual Explanation — How Binary Search Narrows Down
The following diagram shows binary search in action on a sorted list of eight numbers. We want to find the value 23. Watch how the highlighted search range (between low and high) shrinks by half at each step until the target is found.
low, mid, and high pointers. Grayed-out elements have been eliminated. The yellow cell is the current midpoint being compared, and the green cell marks the found target.Notice how quickly the search range collapses. In Step 1, all eight elements are candidates. After one comparison with the midpoint (14), we know 23 must be in the right half. After Step 2's comparison with 31, we narrow it down to a single element. By Step 3 we confirm the match — just 3 comparisons instead of up to 8. This efficiency becomes dramatic with larger datasets: a list of one million items needs at most about 20 comparisons.
SECTION 4
Mathematical Framework — Why Logarithmic?
The speed of binary search can be described precisely using Big-O notation, which tells us how the number of operations grows relative to the input size. Let's see why binary search is O(log n) and how that compares to a simple linear scan.
LINEAR SEARCH TIME COMPLEXITY
In the worst case, a linear search checks every element. If n is the list size, you might need n comparisons.
BINARY SEARCH TIME COMPLEXITY
Each step halves the search range. After k steps, the remaining size is n / 2k. We stop when this equals 1, so k = log₂(n).
MIDPOINT CALCULATION
This formula avoids integer overflow that can occur with (low + high) // 2 when low and high are very large. In Python, integers have unlimited size, but this form is still considered best practice.
| List Size (n) | Linear Search (worst case) | Binary Search (worst case) |
|---|---|---|
| 10 | 10 comparisons | ≈ 4 comparisons |
| 1,000 | 1,000 comparisons | ≈ 10 comparisons |
| 1,000,000 | 1,000,000 comparisons | ≈ 20 comparisons |
| 1,000,000,000 | 1,000,000,000 comparisons | ≈ 30 comparisons |
Comparison of worst-case comparisons for linear vs. binary search
The table makes the difference crystal clear. A billion-element list that would require up to a billion checks with linear search can be handled by binary search in roughly 30 comparisons. That's the power of logarithmic growth — it barely increases even as the input size explodes.
SECTION 5
Python's bisect Module — Binary Search Made Easy
Python ships with a built-in module called bisect that implements binary search for you. Instead of writing the loop yourself (and risking off-by-one bugs), you can call functions from this module to find insertion points or insert values into sorted lists. The module provides four main functions that every Python programmer should know.
bisect_left and bisect_right return insertion indices; insort_left and insort_right actually insert the value into the list.The critical difference between bisect_left and bisect_right matters when the list contains duplicate values. bisect_left returns the index before existing duplicates, while bisect_right returns the index after them. When there are no duplicates, both functions return the same index. The insort functions combine finding the position and inserting the element into one call, keeping the list sorted automatically.
Quick Import
import bisect at the top of your Python file. No installation is needed — it comes with Python's standard library.SECTION 6
Worked Example — Binary Search by Hand and with bisect
Let's walk through a complete example. We have a sorted list of exam scores, and we want to find whether a score of 85 exists and where a new score of 72 should be inserted.
Finding 85 in a Sorted List Using Binary Search
- 1
Step 1 — Set Up the List and Pointers
Our sorted list isscores = [55, 62, 70, 78, 85, 91, 96]. We setlow = 0andhigh = 6(the last valid index). Our target is 85.low = 0, high = 6, target = 85 - 2
Step 2 — First Iteration: Calculate mid
Calculatemid = (0 + 6) // 2 = 3. The value at index 3 isscores[3] = 78. Since 78 < 85, our target is in the right half. Updatelow = mid + 1 = 4.78 < 85 → search right half. low = 4, high = 6 - 3
Step 3 — Second Iteration: Recalculate mid
Calculatemid = (4 + 6) // 2 = 5. The value at index 5 isscores[5] = 91. Since 91 > 85, our target is in the left half. Updatehigh = mid − 1 = 4.91 > 85 → search left half. low = 4, high = 4 - 4
Step 4 — Third Iteration: Found!
Calculatemid = (4 + 4) // 2 = 4. The value at index 4 isscores[4] = 85. This matches our target! The function returns index 4.Found 85 at index 4 in just 3 comparisons. - 5
Step 5 — Using bisect to Insert 72
Now let's use the bisect module. Callingbisect.bisect_left(scores, 72)returns3, meaning 72 should go at index 3 (between 70 and 78). Callingbisect.insort(scores, 72)inserts it directly, producing[55, 62, 70, 72, 78, 85, 91, 96].bisect_left returns 3; insort places 72 at index 3 automatically.
SECTION 7
Strengths, Limitations & Comparisons
Binary search is powerful, but it isn't always the right tool. Understanding when to use it — and when not to — is just as important as knowing how it works. Let's compare it to other approaches and explore its trade-offs.
| Feature | Binary Search | Linear Search |
|---|---|---|
| Time Complexity | O(log n) — very fast | O(n) — slow for large lists |
| Requires Sorted Data? | Yes — must be sorted first | No — works on any list |
| Best For | Large sorted datasets, repeated searches | Small lists, unsorted data, single searches |
| Implementation Difficulty | Moderate — off-by-one errors are common | Easy — simple loop |
| Space Complexity | O(1) — iterative version uses constant space | O(1) |
Binary Search vs. Linear Search
WHEN TO CHOOSE WHICH
Common Pitfall
high = mid instead of high = mid − 1, or checking low < high instead of low <= high. This can cause infinite loops or missed elements. Using Python's bisect module avoids these bugs entirely.SECTION 8
Connection to Advanced Topics
Binary search isn't just for finding items in a list — the core principle of "eliminate half the possibilities" appears everywhere in computer science. Once you master basic binary search, you'll recognize the same pattern in more advanced data structures and algorithms.
| Concept | Connection to Binary Search |
|---|---|
| Binary Search Trees (BSTs) | A tree data structure where each node's left children are smaller and right children are larger — the same halving logic, but stored in a tree instead of a flat list. |
| Binary Search on the Answer | A competitive programming technique where you binary search over possible answer values rather than indices, useful for optimization problems. |
| Merge Sort & Quick Sort | These O(n log n) sorting algorithms use divide-and-conquer — the same recursive halving strategy that makes binary search efficient. |
| Database Indexing | Real databases use B-trees (generalized binary search trees) to look up records without scanning every row — the same log-time idea at massive scale. |
How binary search concepts extend into advanced CS topics
As you continue learning algorithms, pay attention to any time a problem can be framed as searching within a sorted or monotonic space. If you can define a condition that switches from False to True (or vice versa) at some boundary, binary search can likely find that boundary efficiently. This insight turns binary search from a simple list-lookup tool into a versatile problem-solving strategy.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
[8, 3, 11, 1, 7, 15, 2] while searching for 7.Show answer
Question 2: BASIC CALCULATION
[10, 20, 30, 40, 50, 60, 70, 80], trace through binary search to find 60. List the value of low, high, and mid at each step, and state how many comparisons are needed.Show answer
Question 3: INTERMEDIATE
binary_search(arr, target) that returns the index of target in a sorted list arr, or -1 if not found. Do not use the bisect module.Show answer
Question 4: APPLIED
boundaries = [60, 70, 80, 90] and corresponding letter grades ['F', 'D', 'C', 'B', 'A'], use bisect.bisect() to determine the letter grade for a score of 75.Show answer
Question 5: CRITICAL THINKING
Show answer
SUMMARY
Lesson Summary
Summary
Binary search is an efficient algorithm that finds a target value in a sorted list by repeatedly comparing the target to the middle element and discarding half the remaining candidates. This gives it a time complexity of O(log n), meaning a list of one million items needs at most about 20 comparisons — vastly faster than checking every element one by one with linear search's O(n). The algorithm uses two pointers (low and high) and a calculated mid to narrow the search range each iteration.
Python's built-in bisect module provides battle-tested implementations so you don't have to write binary search from scratch. Key functions include bisect_left() and bisect_right() for finding insertion points, and insort() for inserting values while maintaining sort order. Remember: binary search requires sorted data as a precondition, and the divide-and-conquer principle it uses appears throughout computer science — from binary search trees to merge sort to real-world database indexing.