已发布·持续改进
查找 指南 · 4/6
本章目前仅提供英文版。
Choosing a search method is not only about the cost of one lookup. You also have to weigh the cost of preparing the data, the cost of updates, memory use, and which kinds of questions the structure can answer at all. This chapter lays out the costs of linear search, binary search, parametric search, ternary search and hash lookup, and turns them into rules for picking one.
O(1).n / 2 comparisons, O(n).n comparisons, O(n).O(1) extra memory.It needs no preparation and works on unsorted data. Because it reads contiguous memory in order, which caches love, it often beats binary search in practice for a few dozen elements or fewer.
Each comparison halves the range, so the worst case is floor(log2 n) + 1 comparisons: 20 for a million elements, 30 for a billion.
O(1) (the closed-interval form can hit the target at the first midpoint), average and worst O(log n). The lower_bound form always runs close to log n steps.O(1) for the loop version, O(log n) for a recursive version because of the call stack.O(n log n).Counting the steps makes the logarithmic growth visible.
def lower_bound_count(a, x):
lo, hi, steps = 0, len(a), 0
while lo < hi:
steps += 1
mid = (lo + hi) // 2
if a[mid] < x:
lo = mid + 1
else:
hi = mid
return lo, steps
for n in (10, 1_000, 1_000_000):
a = list(range(n))
print(n, lower_bound_count(a, n - 1)[1]) # 3, 10, 20With k queries, linear search costs k * n while sort-then-search costs n log n + k log n. Once the number of queries is well above log n, sorting wins. If you will search a dataset once or twice and then throw it away, do not sort it.
Inserting into or deleting from a sorted array means shifting the elements behind the position, which is O(n). When updates are frequent, a balanced binary search tree, a B-tree, or a library such as Python's sortedcontainers keeps both lookups and updates near O(log n).
If the answer range has size R and evaluating the predicate costs , parametric search costs . A predicate that scans the whole array to test feasibility gives . On real numbers, run iterations for a precision , or simply a fixed count such as 100.
CO(C * log R)O(n log R)log2(R / eps)epsTernary search keeps two thirds of the range per round and evaluates the function twice each time. For the same precision it therefore needs more evaluations than binary search. If the problem is on integers and can be rewritten as a neighbor-comparison binary search, prefer that.
import math
R, eps = 1e9, 1e-6
print(math.ceil(math.log2(R / eps))) # binary: about 50 rounds, 50 evaluations
print(math.ceil(math.log(R / eps, 1.5)) * 2) # ternary: about 86 rounds, about 172 evaluationsA hash table finds a key in O(1) on average, but if every key lands in the same bucket the worst case degrades to O(n). It also uses more memory than an array, and since it keeps no order it cannot answer range queries, "next larger value", or "k-th smallest".
import random
import timeit
from bisect import bisect_left
data = sorted(random.sample(range(10_000_000), 100_000))
as_set = set(data)
probe = data[len(data) // 2]
print(timeit.timeit(lambda: probe in data, number=200)) # linear: slowest
print(timeit.timeit(lambda: bisect_left(data, probe), number=200))
print(timeit.timeit(lambda: probe in as_set, number=200)) # hash: fastestThe exact numbers depend on your machine, but with 100,000 elements linear search is typically thousands of times slower than the other two, or worse.
| Method | Preparation | One lookup | Insert / delete | Range and order queries | Extra memory |
|---|---|---|---|---|---|
| Linear search | none | O(n) | O(1) (append) | possible, but O(n) | O(1) |
| Sorted array + binary search | O(n log n) | O(log n) | O(n) | O(log n + k) | O(1) |
| Balanced BST / B-tree | O(n log n) | O(log n) | O(log n) | O(log n + k) | O(n) |
| Hash table | O(n) | O(1) average | O(1) average | not supported | O(n) |
Here k is the number of results that fall inside the range.
O(n) but needs no preparation, which suits small data and one-off lookups.O(log n); the O(n log n) sorting cost is recovered when there are many queries.O(predicate cost * log range); ternary search needs more evaluations for the same precision.
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。