Separate Chaining Search

Resolve hash collisions by storing multiple elements in buckets using linked structures.

Separate Chaining Search

Separate chaining handles collisions in a hash table by storing all elements that hash to the same index in a bucket. Each bucket is typically a linked list, though dynamic arrays or trees are also used.

You search by hashing the key and scanning only the corresponding bucket.

Problem

Given a hash table with chaining and a key $k$, find the associated value if it exists.

Model

  • Table $T$ has size $m$
  • Hash function $h(k)$ maps keys to indices
  • Each $T[i]$ stores a list of entries

Compute:

$$ i = h(k) $$

Then search inside bucket $T[i]$.

Algorithm

chaining_search(T, k):
    i = h(k)
    for each (key, value) in T[i]:
        if key == k:
            return value
    return NOT_FOUND

Example

Let $m = 5$ and:

$$ h(k) = k \bmod 5 $$

Insert:

$$ [12, 7, 22] $$

Buckets:

index bucket
2 (12 → 7 → 22)

Search for $k = 22$:

  • compute $h(22) = 2$
  • scan bucket: 12, 7, 22
  • match found

Correctness

All keys that hash to index $i$ are stored in bucket $T[i]$. The algorithm examines every element in that bucket. If $k$ exists, it must appear there, so it will be found.

Complexity

Let $\alpha = n/m$ be the load factor.

case time
average $O(1 + \alpha)$
worst $O(n)$

When $\alpha$ remains bounded, lookup runs in constant expected time.

Space

$$ O(n + m) $$

Memory includes the table and all stored elements.

When to Use

Separate chaining is appropriate when:

  • table resizing is expensive or infrequent
  • deletion operations must be simple
  • load factor may grow beyond 1
  • memory overhead from pointers is acceptable

It handles high load factors better than open addressing.

Implementation

class HashTable:
    def __init__(self, m=8):
        self.m = m
        self.table = [[] for _ in range(m)]

    def _hash(self, key):
        return hash(key) % self.m

    def get(self, key):
        i = self._hash(key)
        for k, v in self.table[i]:
            if k == key:
                return v
        return None
type Entry[K comparable, V any] struct {
	Key   K
	Value V
}

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

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

func (h *HashTable[K, V]) Get(key K) (V, bool) {
	i := h.hash(key)

	for _, e := range h.Table[i] {
		if e.Key == key {
			return e.Value, true
		}
	}

	var zero V
	return zero, false
}