List Intersection and Search
How sorted lists (inverted lists, sorted join columns) are intersected and merged: the linear-time zipper, binary search and galloping (exponential) search intersection, and when to use which.
Concepts#
- List Intersectionthe operation and its algorithms: zipper (intersect/merge), binary search and galloping search intersection, multi-list intersection start here
- Exponential Searchgalloping search for one value in a sorted array in ; the building block of the fastest intersection
How it fits together#
One principle, three uses. The zipper walks two sorted lists with one pointer each and advances the pointer at the smaller element. The same loop serves:
| Use | Lists | Output on equal elements | Lecture |
|---|---|---|---|
| AND keyword query | inverted lists | emit the ID (intersection) | DBIS_VL01 > Query Processing |
| OR keyword query / ranking | inverted lists with scores | emit once, sum the scores (merge) | DBIS_VL02 > Ranking: Basic Idea |
| Merge join in SQL | sorted join columns (value, row index) | emit all combinations of rows with that value | DBIS_VL05 > Merge join |
From linear to sublinear. The zipper touches every element of both lists. If , most of the long list is skipped anyway, so it is cheaper to search each of the elements in the long list:
| Algorithm | Time | Search per element of the short list |
|---|---|---|
| Zipper | none (linear scan) | |
| Binary search intersection | binary search over the whole long list | |
| Galloping search intersection | Exponential Search from the previous position, then binary search |
Galloping is asymptotically never worse than the zipper (, from ) and never worse than binary search (). In practice the zipper has the smaller constant factor, so: zipper if the lengths are similar, galloping if they differ by more than a factor of about 10 (List Intersection > Practical choice).
More than two lists. Intersect pairwise, shortest lists first, so the intermediate results stay short; by Zipf’s Law list lengths vary enormously. Alternatively a -way intersect/merge with a priority queue in .
Key Formulas#
- Zipper: ; binary search intersection: ; galloping: — List Intersection > Formula
- Galloping search for at position : ; number of doubling jumps — Exponential Search > Properties
Exercises & Exam Questions#
SuccessSolution
def intersect(list1, list2):
if len(list1) > len(list2):
list1, list2 = list2, list1
result = []
for x in list1:
left, right = 0, len(list2)
while left < right:
mid = int((right - left)/2) + left
if list2[mid] > x:
right = mid
elif list2[mid] < x:
left = mid + 1
else:
result.append(x)
break
return result
- Swap so that
list1is the shorter list (length ). - For each element in the shorter list, binary search in the longer list (length ): each, in total.
SuccessSolution
- Consider instead of ; the difference is only a constant factor.
- implies for all .
- With : .
- Therefore .
- Hence .
SuccessSolution
If the lists differ in length by more than a factor of, say, 10 (), use galloping search; otherwise use the zipper. Galloping search is asymptotically always better or equal, but it is a more complex algorithm with a larger constant factor. Only if does the asymptotic advantage pay off; otherwise the simpler zipper is faster in practice (as observed in the exercises). (The official solution writes “ less than 10”, meaning .)
SuccessSolution
The number of jumps is .
- After jumps the position checked is , and the search stops there, so .
- The position before that is , where it did not stop, so .
- Hence .
SuccessSolution
Exponential phase: check positions until ; then binary search in .
- Since : , hence and . The exponential phase takes steps.
- Hence . The binary search halves the interval in every step: steps.
- Together: .
SuccessSolution
def exponential_search(A, x):
i = 0
while A[i] < x:
i = min(max(2 * i, 1), len(A) - 1)
return i
Start at index 0 and double (max(2*i, 1) advances from 0 to 1), clamped to len(A) - 1. The loop runs times because doubles in each iteration; it stops at the first probed , and the previous probe gives .
SuccessSolution
def merge(A, B):
i = j = 0
res = []
while i < len(A) or j < len(B):
if j == len(B) or (i < len(A) and A[i] <= B[j]):
res.append(A[i])
i += 1
else:
res.append(B[j])
j += 1
return res
Two-pointer merge (zipper): at each step take the smaller of A[i] and B[j] (or whichever list has elements left). Every element is processed once: .
Occurrence in past exams#
| Exam | Task | Points | Subtopics |
|---|---|---|---|
| DBIS Exam WS1819 (not published) | Task 1 | 20 | binary search intersection (code), galloping vs. zipper proof, practical choice |
| DBIS Exam WS1920 (not published) | Task 1.3 | 10 | exact number of jumps of exponential search |
| DBIS Exam WS2122 (not published) | Task 1.4 | 10 | galloping search in (proof) |
| DBIS Exam WS2223 (not published) | Task 1.1 | 5 | implement exponential search |
| DBIS Exam WS2324 (not published) | Task 1.2 | 10 | merge two sorted lists (code) |
Not in WS 13/14 – WS 17/18 or the WS 23/24 retake. Pattern: one 5–10 point subtask (code or proof) inside a larger indexing task; galloping/exponential search was never lectured in WS 25/26, the zipper and merging were.
Lectures#
- DBIS_VL01 — query processing: zipper intersection of two inverted lists, merging, lists
- DBIS_VL05 — merge join of sorted join columns via the zipper algorithm
Related Topics#
- Inverted Index and Ranking — provides the sorted inverted lists; ranking merges scored lists
- SQL and Query Planning — the merge join is list intersection on sorted join columns
- Encodings and Compression — inverted lists are stored gap-encoded and compressed