06 Hash Tables

A progressive guide to hash tables that explains hashing, collisions, collision-resolution strategies, load factors, resizing, performance limits, and practical data-structure choices.

From keys to table positions

A stores key–value pairs in an array. To place or find a key, the table applies a and converts the resulting hash code into a valid slot, often with

index(k)=h(k) mod m,\text{index}(k)=h(k)\bmod m,

where mm is the number of slots.

For example, with 1010 slots and h(k)=kh(k)=k, key 4242 initially maps to slot 22, since 42 mod 10=242\bmod 10=2. This mapping is not necessarily unique: different keys may produce the same index.

A good has four important properties:

  • Deterministic: the same key produces the same hash value during a table operation.

  • Fast: computing the hash does not dominate lookup cost.

  • Well distributed: unrelated keys are spread across the table rather than clustered.

  • Consistent with equality: equal keys must have equal hash values.

Data-structure hashing prioritizes speed and distribution. It is not necessarily cryptographic hashing, which additionally aims to resist intentional attacks and make inputs difficult to recover or manipulate.

The expected O(1)O(1) cost often associated with a depends on these properties and on maintaining a suitable . It is not a worst-case guarantee.

Takeaway: Hashing provides a fast route from a key to a likely table position, but it does not guarantee a unique position.

Collisions and resolution strategies

A occurs when distinct keys map to the same index. For a table with 1010 slots, keys 1212 and 4242 collide because

12 mod 10=42 mod 10=2.12\bmod 10=42\bmod 10=2.

Collisions are unavoidable when many possible keys are mapped into a finite array. The table must therefore choose a -resolution strategy. The two central approaches are and .

The distinction is where colliding entries are stored:

  • In , a slot refers to a collection of entries.

  • In , all entries remain in the main array and the algorithm probes other slots.

A hash match is not proof that two keys are equal. After locating a candidate slot or entry, the implementation must compare the keys themselves.

Takeaway: A is effective not because collisions disappear, but because it handles them efficiently.

gives each table slot a collection, often a linked list. If several keys map to index 22, they can appear in that slot's chain in sequence, such as (12,A)(12,A), (42,B)(42,B), and (72,C)(72,C).

For lookup, the table computes the index, traverses that chain, and compares keys until it finds the target or reaches the end. Insertion searches the chain first: an existing key normally has its value replaced, while a new key is added to the chain. Deletion removes the matching entry directly, without requiring a special marker.

If there are nn entries and mm chains, the is

α=nm.\alpha=\frac{n}{m}.

Here, α\alpha represents the average number of entries per chain and may be greater than 11. Under a simple uniform-hashing assumption, expected search and update time is Θ(1+α)\Theta(1+\alpha).

Advantages include straightforward deletion, the ability to store more entries than there are slots, and gradual performance degradation as chains grow. The trade-offs are extra memory for chain nodes or collections and potentially poor cache locality. A poor can still create a chain of length nn.

Takeaway: Chaining makes deletion simple and tolerates load factors above 11, but long chains increase work.

and probing

stores every entry directly in the table array. After a , the algorithm follows a probe sequence to inspect alternative slots. Because an unsuccessful search must eventually encounter an empty slot, the must satisfy

α=nm<1.\alpha=\frac{n}{m}<1.

With linear probing, the probe positions are

pi(k)=(h(k)+i) mod m,p_i(k)=(h(k)+i)\bmod m,

for i=0,1,2,…i=0,1,2,\ldots. The algorithm checks the initial position, then successive positions, wrapping around when it reaches the end of the array. This layout is cache-friendly, but occupied runs can form primary clustering, which increases later probe lengths.

Other strategies change how alternative positions are selected:

  • Quadratic probing uses increasingly larger offsets, such as h(k)+12h(k)+1^2, h(k)+22h(k)+2^2, and so on.

  • Double hashing uses a second for the step size:

    pi(k)=(h1(k)+i h2(k)) mod m.p_i(k)=(h_1(k)+i\,h_2(k))\bmod m.

Deletion requires care. If a slot is simply cleared, a later search may stop there even though its target lies farther along the probe sequence. A marks the slot as previously occupied and tells searches to continue. Tombstones preserve correctness but may lengthen probes, so rebuilding can be useful.

Takeaway: saves separate chain storage and can be cache-friendly, but it requires careful probing, deletion, and occupancy control.

, resizing, and rehashing

The measures how full a table is:

α=nm,\alpha=\frac{n}{m},

where nn is the number of stored entries and mm is the table capacity. Its meaning depends on the strategy:

  • With , it is the average number of entries per chain and may exceed 11.

  • With , it is the fraction of occupied slots and must remain below 11.

As α\alpha increases, collisions and search costs generally increase. For linear probing, probe counts can rise sharply as the table approaches full capacity. A lower usually improves speed but uses more memory; a higher saves memory but increases and probe costs.

A table typically resizes when α\alpha crosses a threshold. means creating a larger array, recomputing each index using the new capacity, and reinserting every entry. Recomputing is necessary because changing mm changes the remainder. For instance, an entry mapped by h(k) mod 8h(k)\bmod 8 may move when the capacity becomes 1616.

If a capacity of 88 has a threshold of 0.750.75, the threshold count is 0.75×8=60.75\times 8=6. Inserting a seventh entry may trigger growth to capacity 1616. One resize costs O(n)O(n), but geometric growth spreads these costs across many insertions, giving amortized O(1)O(1) insertion time.

Takeaway: Resizing trades occasional rebuilding work for shorter chains or probe sequences during ordinary operations.

Performance and when to choose a

With a well-distributed , a controlled , and an appropriate strategy, the usual performance profile is:

  • Search: expected O(1)O(1); worst-case O(n)O(n).

  • Insert: expected amortized O(1)O(1); worst-case O(n)O(n), including possible resizing work.

  • Delete: expected O(1)O(1); worst-case O(n)O(n).

  • Resize: O(n)O(n) when all entries are moved and reinserted.

The worst case can occur when many keys collide or when clustering creates long probe sequences. Performance is influenced by hash-function quality, , strategy, equality checks, resizing policy, and adversarial input.

A is a strong choice for dictionaries and maps, sets of unique values, caches, symbol tables, frequency counting, indexes, and membership tests when fast lookup by key is more important than sorted order. A balanced search tree is often preferable when the application needs sorted traversal, minimum or maximum operations, predecessor or successor queries, or guaranteed O(log⁡n)O(\log n) worst-case operations.

Use the following decision rule:

  1. Choose a when direct key-based access and expected fast operations are the main goals.

  2. Choose when straightforward deletion, flexible occupancy, or gradual degradation is valuable.

  3. Choose when compact storage and cache-friendly access are important and occupancy can be controlled.

  4. Choose a balanced search tree when sorted order or guaranteed logarithmic worst-case operations are required.

Final takeaway: Hash tables offer expected constant-time key-based operations, but their performance depends on distribution, occupancy, handling, and resizing rather than on the name of the data structure alone.