6.1 Searching Algorithms: Linear Search and Binary Search
Key Takeaways
- Linear search works on unsorted or sorted data; it examines k + 1 elements to find a target at index k and all n elements when the target is absent.
- Binary search requires sorted data and random access by index; each comparison eliminates about half of the remaining elements.
- Binary search examines at most ⌊log₂ n⌋ + 1 elements, which is 10 for 1,000 elements and 20 for 1,000,000 elements.
- A correct binary search loops while low ≤ high and moves the bounds to mid + 1 or mid − 1, so the element just checked is excluded.
- For a single search of unsorted data, linear search is best; sorting first pays off only when many searches will follow.
What this competency asks
ETS groups searching with sorting: understand searching and sorting algorithms; analyze searching algorithms for correctness and efficiency. For searching, you should be able to:
- Trace algorithms and predict output and intermediate results.
- Calculate the number of comparisons required for linear and binary search.
Linear (sequential) search
Linear search checks each element in order until it finds the target or runs out of elements.
// returns the index of target in list, or -1 if it is absent
int linearSearch ( int[ ] list, int n, int target )
for ( int i ← 0; i < n; i ← i + 1 )
if ( list[i] == target )
return i
end if
end for
return -1
end linearSearch
| Situation | Elements examined |
|---|---|
| Target at index 0 (best case) | 1 |
| Target at index k | k + 1 |
| Target absent (worst case) | n |
| Average, if every position is equally likely | (n + 1) / 2 |
Linear search is O(n). It needs no precondition: the data may be unsorted, and it works on structures that can only be walked in order, such as linked lists. If the list is known to be sorted, you can stop early once an element exceeds the target. That shortens unsuccessful searches, but the worst case is still O(n).
Binary search
Binary search works only on sorted data. It compares the target with the middle element of the current range and discards the half that cannot contain the target.
// list is sorted in increasing order; first element at index 0
int binarySearch ( int[ ] list, int n, int target )
int low ← 0
int high ← n - 1
while ( low ≤ high )
int mid ← ( low + high ) / 2 // integer division
if ( list[mid] == target )
return mid
else
if ( list[mid] < target )
low ← mid + 1
else
high ← mid - 1
end if
end if
end while
return -1
end binarySearch
Worked trace
Sorted list (indexes 0–9): {3, 8, 12, 17, 23, 31, 38, 44, 50, 61}
Search for 44
| low | high | mid | list[mid] | Decision |
|---|---|---|---|---|
| 0 | 9 | 4 | 23 | 23 < 44, so low ← 5 |
| 5 | 9 | 7 | 44 | Found; return 7 |
Two elements examined: 23 and 44.
Search for 20 (absent)
| low | high | mid | list[mid] | Decision |
|---|---|---|---|---|
| 0 | 9 | 4 | 23 | 23 > 20, so high ← 3 |
| 0 | 3 | 1 | 8 | 8 < 20, so low ← 2 |
| 2 | 3 | 2 | 12 | 12 < 20, so low ← 3 |
| 3 | 3 | 3 | 17 | 17 < 20, so low ← 4 |
| 4 | 3 | — | — | low > high; return −1 |
Four elements examined: 23, 8, 12, 17. For n = 10, the maximum is ⌊log₂ 10⌋ + 1 = 3 + 1 = 4.
Counting comparisons
Each pass through the loop examines one element and at least halves the remaining range, so binary search examines at most ⌊log₂ n⌋ + 1 elements. This equals ⌈log₂(n + 1)⌉.
| n | Linear search, worst case | Binary search, worst case |
|---|---|---|
| 10 | 10 | 4 |
| 100 | 100 | 7 |
| 1,000 | 1,000 | 10 |
| 10,000 | 10,000 | 14 |
| 1,000,000 | 1,000,000 | 20 |
| 1,000,000,000 | 1,000,000,000 | 30 |
A quick way to calculate: find the smallest power of 2 that is greater than n, and use its exponent. For n = 1,000,000: 2¹⁹ = 524,288 ≤ n and 2²⁰ = 1,048,576 > n, so the maximum is 20. For exactly n = 16, 2⁴ = 16 is not greater than 16, so the maximum is 5.
Some questions count three-way comparisons ("equal, less, or greater?") as one comparison, as above. Others count each == and < test separately. Read the stem's definition.
Correctness details
- Precondition: sorted data. On unsorted data, binary search can report that a present value is missing.
- Loop condition
low ≤ high. When low equals high, one element remains and must still be checked. Using<skips it. - Bounds
mid + 1andmid - 1. The middle element was already checked. Settinglow ← midcan cause an infinite loop when high = low + 1. - Duplicates. Standard binary search returns some matching index, not necessarily the first. Finding the first occurrence requires continuing to search the left half after a match.
- Integer overflow. In languages with fixed-size integers,
( low + high ) / 2can overflow for enormous arrays.low + ( high - low ) / 2gives the same midpoint without the risk.
Choosing between them
| Factor | Linear search | Binary search |
|---|---|---|
| Data must be sorted? | No | Yes |
| Needs index (random) access? | No | Yes |
| Worst case | O(n) | O(log n) |
| Best case | 1 element | 1 element |
| Simple to write correctly? | Very | Requires care with bounds |
If the data are unsorted and you will search once, use linear search. Sorting first costs O(n log n), which is more than the O(n) search. If you will search many times, sort once and then use binary search. The sorting cost is spread across all the fast searches.
Binary search, using integer division mid ← ( low + high ) / 2, looks for 31 in the sorted list {4, 9, 15, 22, 31, 40, 57} (indexes 0–6). Which elements are compared with 31, in order?
A sorted array contains 1,000,000 distinct values. What is the maximum number of elements binary search must examine to find a value or determine that it is absent?
A program receives an unsorted list of 50,000 records and must look up one record exactly once. Which approach is most efficient?