Skip to content

Cuckoo Hashing Lookup

Look up a key in a cuckoo hash table by checking a small fixed set of candidate positions.

Cuckoo hashing stores each key in one of several possible table positions. In the common two hash version, a key can live at either h1(k)h_1(k) or h2(k)h_2(k). Lookup is therefore simple: compute both positions and check them.

The search operation is fast because it does not scan a bucket or probe a long sequence. The cost is moved mostly to insertion, where an existing key may be displaced and moved to its alternate position.

Problem

Given a cuckoo hash table and a key kk, return the associated value if it exists.

Model

A two hash cuckoo table has:

  • table TT
  • size mm
  • hash functions h1h_1 and h2h_2

A stored key kk must satisfy:

k is stored at T[h1(k)] or T[h2(k)] k \text{ is stored at } T[h_1(k)] \text{ or } T[h_2(k)]

Algorithm

cuckoo_lookup(T, k):
    i = h1(k)
    if T[i] is occupied and T[i].key == k:
        return T[i].value

    j = h2(k)
    if T[j] is occupied and T[j].key == k:
        return T[j].value

    return NOT_FOUND

Example

Let:

  • m=11m = 11
  • h1(k)=kmod11h_1(k) = k \bmod 11
  • h2(k)=1+(kmod10)h_2(k) = 1 + (k \bmod 10)

For key 2424:

h1(24)=2 h_1(24) = 2 h2(24)=5 h_2(24) = 5

Lookup checks only positions 2 and 5.

stepindexresult
12no match
25match

So the value stored with key 2424 is returned.

Correctness

The cuckoo hashing invariant says that every stored key appears in one of its candidate positions. For the two hash version, these positions are exactly h1(k)h_1(k) and h2(k)h_2(k).

The lookup checks both candidate positions. If either contains kk, it returns the associated value. If neither contains kk, then the invariant implies that kk is not present in the table.

Complexity

casetime
worstO(1)O(1)
averageO(1)O(1)

For a fixed number of hash functions, lookup checks a fixed number of cells.

Space

O(m) O(m)

The table stores entries directly. Practical cuckoo hash tables often keep the load factor below a chosen threshold to avoid insertion cycles and frequent rebuilds.

When to Use

Cuckoo hashing lookup is appropriate when:

  • worst case constant lookup time matters
  • lookups are much more frequent than insertions
  • predictable memory access is useful
  • the implementation can tolerate more complex insertion logic

It is less convenient when insertions are extremely frequent or when resizing and rehashing must be avoided.

Implementation

class CuckooHashTable:
    def __init__(self, m=16):
        self.m = m
        self.table = [None] * m

    def _h1(self, key):
        return hash(("h1", key)) % self.m

    def _h2(self, key):
        return hash(("h2", key)) % self.m

    def get(self, key):
        i = self._h1(key)
        if self.table[i] is not None:
            k, v = self.table[i]
            if k == key:
                return v

        j = self._h2(key)
        if self.table[j] is not None:
            k, v = self.table[j]
            if k == key:
                return v

        return None
type Entry[K comparable, V any] struct {
	Key   K
	Value V
	Used  bool
}

type CuckooHashTable[K comparable, V any] struct {
	Table []Entry[K, V]
	M     int
}

func (h *CuckooHashTable[K, V]) h1(key K) int {
	return int(uintptr(any(key))) % h.M
}

func (h *CuckooHashTable[K, V]) h2(key K) int {
	return (int(uintptr(any(key))) / h.M) % h.M
}

func (h *CuckooHashTable[K, V]) Get(key K) (V, bool) {
	i := h.h1(key)
	if h.Table[i].Used && h.Table[i].Key == key {
		return h.Table[i].Value, true
	}

	j := h.h2(key)
	if h.Table[j].Used && h.Table[j].Key == key {
		return h.Table[j].Value, true
	}

	var zero V
	return zero, false
}