Study Vault Search notes, aliases and full text
Graph
ConceptDBISAI-written · not yet proofread

List Intersection

List intersection computes the sorted list of elements common to two (or more) sorted lists, e.g. the doc IDs matching all keywords of a query, in linear time with the zipper algorithm or faster with binary or galloping search when one list is much shorter.

Know these firstInverted Index

Formula#

AlgorithmRuntimeBest when
Zipper (linear)O(k+n)O(k + n)k≈nk \approx n
Binary searchO(k⋅log⁡n)O(k \cdot \log n)k≪nk \ll n
Galloping searchO(k⋅log⁡(1+n/k))O(k \cdot \log(1 + n/k))k≪nk \ll n (tighter bound)
mm-way intersect/merge of L1,…,LmL_1, \dots, L_mO(log⁡m⋅∑i∣Li∣)O(\log m \cdot \sum_i \lvert L_i \rvert)many lists at once

Properties#

1.Zipper (Linear Merge Intersection)#

Walk through both sorted lists simultaneously with two pointers. Advance the pointer pointing to the smaller element; when both point to equal elements, emit that element to the result (Slides 01, p. 18).

def intersect(list1, list2):
    result = []
    i, j = 0, 0
    while i < len(list1) and j < len(list2):
        if list1[i] < list2[j]:
            i += 1
        elif list1[i] > list2[j]:
            j += 1
        else:
            result.append(list1[i])
            i += 1
            j += 1
    return result

Runtime: O(k+n)O(k + n): every step advances at least one pointer.

2.Binary Search Intersection#

For each element of the shorter list, perform a binary search in the longer list.

def intersect_binary(list1, list2):
    if len(list1) > len(list2):
        list1, list2 = list2, list1
    result = []
    for x in list1:
        lo, hi = 0, len(list2)
        while lo < hi:
            mid = (lo + hi) // 2
            if list2[mid] < x:
                lo = mid + 1
            elif list2[mid] > x:
                hi = mid
            else:
                result.append(x)
                break
    return result

Runtime: O(k⋅log⁡n)O(k \cdot \log n): kk binary searches of O(log⁡n)O(\log n) each.

3.Galloping (Exponential) Search Intersection#

A refinement of binary search: for each element xx of the shorter list, run an exponential (galloping) search in the longer list: find a range [2j−1,2j][2^{j-1}, 2^j] in which xx could lie by doubling the index, then binary search within that range.

Runtime: O ⁣(k⋅log⁡ ⁣(1+nk))O\!\left(k \cdot \log\!\left(1 + \frac{n}{k}\right)\right)

Galloping is never worse than the zipper#

Using ex≥1+xe^x \geq 1 + x, i.e. ln⁡(1+x)≤x\ln(1 + x) \leq x, with x=n/kx = n/k:

k⋅ln⁡ ⁣(1+nk)≤k⋅nk=n≤k+nk \cdot \ln\!\left(1 + \frac{n}{k}\right) \leq k \cdot \frac{n}{k} = n \leq k + n

log⁡\log and ln⁡\ln differ only by a constant factor, hence O(k⋅log⁡(1+n/k))⊆O(k+n)O(k \cdot \log(1 + n/k)) \subseteq O(k + n) (DBIS Exam WS1819 (not published), Task 1.2).

Practical choice#

Galloping search has a larger constant factor due to its more complex algorithm. When k/nk/n is close to 1, the zipper’s simplicity wins in practice. A common heuristic: use galloping when n/k>10n/k > 10, otherwise use the zipper (DBIS Exam WS1819 (not published), Task 1.3).

Multi-List Intersection (m>2m > 2 lists)#

Given inverted lists L1,L2,…,LmL_1, L_2, \ldots, L_m for mm query terms (Slides 01, p. 19):

  1. Sequential pairwise: Intersect L1∩L2→L12L_1 \cap L_2 \to L_{12}, then L12∩L3→L123L_{12} \cap L_3 \to L_{123}, etc.
  2. Optimization: Sort lists by length (∣L1∣≤∣L2∣≤⋯≤∣Lm∣|L_1| \leq |L_2| \leq \cdots \leq |L_m|) to minimize intermediate result sizes.
  3. mm-way intersection using a priority queue: O(log⁡m⋅∑i∣Li∣)O(\log m \cdot \sum_i |L_i|).

Example#

Lists for query “spielberg dinosaurs” (Slides 01, p. 18):

L1(spielberg):6, 22, 24, 53, 62, 102, 148, 194, 197,…L_1 (\text{spielberg}): \quad 6,\ 22,\ 24,\ 53,\ \boxed{62},\ 102,\ 148,\ \boxed{194},\ 197, \ldotsL2(dinosaurs):62, 194, 404, 560, 582, 713, 838, 1053,…L_2 (\text{dinosaurs}): \quad \boxed{62},\ \boxed{194},\ 404,\ 560,\ 582,\ 713,\ 838,\ 1053, \ldotsL1∩L2={62, 194, …}L_1 \cap L_2 = \{62,\ 194,\ \ldots\}

The zipper walks both lists left to right: L1L_1’s pointer advances over 6,22,24,536, 22, 24, 53 (all <62< 62), both pointers meet at 6262 (emit), L1L_1 advances over 102,148102, 148 (all <194< 194), both meet at 194194 (emit), and so on.

Sources#

Search

Type In
↑ ↓ move ↵ open ⇥ next section Ctrl↵ results page Prefix # to search text only