Skip to content
SimpleScope

Data structures · 16 / 19

Hash table visualization

A hash table with chaining turns a key into a bucket number with a hash function and keeps the keys of each bucket in a linked list, so lookups take constant time on average.

Time: best case O(1), worst case O(n). Space: O(n). What Big-O notation means.

Last updated

About this lesson
Concept it teaches
Hash function, buckets, chaining and collisions
Canonical source
Cormen et al., Introduction to Algorithms, sections 11.2 (chaining) and 11.3 (the division method)
Builds on
Linked list

I already know this: go to the check

See

+10 XP

Watch the real code run, one step at a time. Change the input and see what happens.

Predict first. Before you press Play, guess which bucket the first number will go to, using h(k) = k mod 7.

Run Hash table

Size
0
Load factor
0.00
Step
1/45
  • Current
  • Comparing
  • Done
  • Ruled out
Looking for 30

Hash table with 7 buckets, h(k) = k mod 7. Each bucket lists its chain from the first node. bucket 0: empty; bucket 1: empty; bucket 2: empty; bucket 3: empty; bucket 4: empty; bucket 5: empty; bucket 6: empty. Waiting: 18, 41, 22, 9, 30, 58, 15. Removed: empty.

The table has 7 empty buckets and the hash function h(k) = k mod 7. Insert the numbers one at a time, then search for the target, remove it, and search again.

The table has 7 empty buckets and the hash function h(k) = k mod 7. Insert the numbers one at a time, then search for the target, remove it, and search again.

Step 1/45comparisonlink change

With focus on the player: Space play · ← → step · Home End jump

Try your own input

Up to 16 whole numbers from -99 to 99, separated by commas or spaces.

A typical table: a few keys share a bucket, so the search walks a short chain.

Understand

+15 XP

The idea in plain words: why it works, what it costs, when to use it. One question at the end.

A hash table stores keys so that you can add, find and remove one without looking at the others. It keeps an array of buckets and a hash function that turns any key into a bucket number. This lesson uses separate chaining: each bucket holds a linked list (the chain) of the keys that hash to it. The function here is h(k) = k mod 7, with 7 buckets.

How it works

  1. insert(k) computes h(k), makes a node for k, and puts it at the front of that bucket's chain.
  2. search(k) computes h(k) and walks only that chain, comparing each key with k.
  3. remove(k) computes h(k) and walks that chain with prev and cur, exactly as in the linked list, then unlinks the node.

When two keys have the same remainder, they collide: they share a bucket and a chain. The table does not break; it just has a longer chain to walk.

Time and space complexity

Complexity
Best case timeO(1)
Average case timeO(1)
Worst case timeO(n)
Extra spaceO(n)
  • Average, O(1): the hash costs the same for every key, and if the keys spread out, a chain holds about n / m nodes, where m is the number of buckets. That ratio is the load factor. Real tables add buckets as it grows, to keep chains short.
  • Worst, O(n): every key lands in the same bucket. The table is then one chain of n nodes, and search walks all of it. Try the preset "Everything in one bucket".
  • Space, O(n): one node per key, plus the array of buckets.

A worked example

Insert the keys 50, 700, 76, 85, 92, 73 and 101 into a table of 7 empty buckets, with h(k) = k mod 7. The table lists the bucket each key hashes to, and the chain of that bucket afterwards, first node first.

Bucket and chain after each insert
Inserth(k)Chain of that bucket afterwards
50150
7000700
76676
85185, 50 (collision with 50)
92192, 85, 50 (collision)
73373
1013101, 73 (collision with 73)

After the seven inserts, bucket 0 holds 700, bucket 1 holds 92, 85, 50, bucket 3 holds 101, 73, bucket 6 holds 76, and buckets 2, 4 and 5 are empty. The load factor is 7 / 7 = 1.00 and the longest chain has 3 nodes.

search(50) computes h(50) = 1 and walks only that chain: it compares 92, 85 and 50, which is 3 comparisons, and it never looks at the other six buckets. remove(50) walks the same chain, stops with prev at 85 and makes 85's link skip the node. A second search(50) compares 92 and 85, reaches the end of the chain and returns null. A linear scan of 7 keys could need up to 7 comparisons for every search; here the work depends on the chain, not on the table.

Pseudocode

T is an array of m buckets, each NIL or the first node of a chain, and x.key and x.next are a node's key and link. The hash is the division method from CLRS section 11.3, and the extra + m keeps a negative key in range.

HASH(k, m)
    return ((k mod m) + m) mod m

CHAINED-INSERT(T, k)
    i = HASH(k, T.length)
    x = a new node with key k
    x.next = T[i]
    T[i] = x

CHAINED-SEARCH(T, k)
    x = T[HASH(k, T.length)]
    while x != NIL and x.key != k
        x = x.next
    return x                  // NIL if absent

CHAINED-DELETE(T, k)
    i = HASH(k, T.length)
    prev = NIL
    cur = T[i]
    while cur != NIL and cur.key != k
        prev = cur
        cur = cur.next
    if cur == NIL
        return false
    if prev == NIL
        T[i] = cur.next
    else
        prev.next = cur.next
    return true

Insertion has no loop, so it is O(1) even when the chain is long. Search and delete walk one chain, whose length is about the load factor on average.

When to use it, and when not to

  • Use it to look things up by key: a dictionary, a set, a cache, counting how often each word appears.
  • Avoid it when you need the keys in order, or all keys between two values. A hash scatters neighbors across buckets, so binary search on a sorted array does that better.

Where it is used

  • Dictionaries and sets in languages. Python's dict and set and Java's HashMap are hash tables. They differ in how they handle a collision: Java keeps a chain in each bucket, as this lesson does, and Python probes for another free slot in the same array, a method called open addressing. Both give O(1) lookups on average.
  • Caches and memoization. A program stores the result of a costly call under its input as the key, and later calls with the same input read the stored result instead of repeating the work. Only exact-key lookup is needed, which is what a hash table does best.
  • Counting, duplicates and symbol tables. Counting words is a table from word to count. Finding duplicates is one pass that checks each value and inserts it. A compiler keeps a symbol table that maps each identifier name in the program to what it knows about it, an example CLRS gives in chapter 11.

Common mistake and edge cases

Variations and related ideas

Open addressing stores every key in the bucket array itself and probes other slots on a collision, so it needs no linked list but must keep the table partly empty. Real tables resize: when the load factor passes a limit, they allocate more buckets and move every key to the bucket its hash gives in the new size, which keeps chains short. Better hash functions, such as universal hashing, protect against keys chosen to collide (CLRS, section 11.3). If you need order, ranges or a guaranteed worst case, a binary search tree trades the O(1) average for O(log n) on a balanced tree. For fixed data that is searched often, a sorted array with binary search needs no extra structure.

One question to finish

A table has 7 buckets and h(k) = k mod 7. Which bucket does the key 27 go to?

Predict

+20 XP

The run stops and asks what happens next. The real run says if you were right.

Practice

+30 XP

Order the lines, fill in the gap, trace it by hand. The real run corrects every answer.

Check yourself

Four questions from memory. They also join your daily review. Questions return on a spaced schedule: right answers come back later, misses come back tomorrow.