ma'am audrey's lessons

ITC 120 › Final › Hashing

Hashing · Lesson 1 of 1

Hashing and Hash Tables

This lesson teaches:TechniqueData structure

Turn a key into a slot number, so a search can jump straight to it. Then learn what to do when two keys want the same slot.

By the end, you can:

  1. explain what hashing, a hash function, a hash table, and a collision are;
  2. place keys into a table by hand with h(k) = k mod m, using separate chaining, linear probing, quadratic probing, and double hashing;
  3. search a hash table, and explain why a deleted slot gets a mark instead of being emptied;
  4. work out the load factor, and explain when and how a table is rehashed;
  5. read and complete a hash table in Java;
  6. compare a hash table's average and worst-case speed with linear search, binary search, and a BST, and choose when to use it.

You need: the Arrays and Linked lists lessons, the Searching lesson, the Maps lesson (your first look at hashing), and scratch paper.

Part 1 · The idea

Jump straight to the right place

You already know two ways to find a value: . Linear search may check every value. Binary search is much faster, but the values must be sorted, and it still needs about 20 checks for a million values.

What if one small calculation told you exactly where a value is? Then you would look in one place only. Offices do this with paper every day:

Think of the registrar's trays

During enrollment, the registrar puts forms into 10 trays, numbered 0 to 9. The rule: use the last digit of the student ID. Form 2025-0347 goes into tray 7. Later, to find that form, the staff do not search all the trays. They use the same rule and look in tray 7 only.

The registrar's 10 traysEnrollment formID 2025-0347Rule: tray = last digit20250347 mod 10 = 7(last digit = the remainder)0123456789trays 0 to 9straight to tray 7ID 2025-0127 also ends in 7: same tray.Both forms stay in tray 7, one on top of the other.To find 2025-0347 later: look in tray 7 only.
The rule turns an ID into a tray number. Finding the form means looking in one tray, not all ten.

Hashing

Hashing means using a fixed rule to turn a key into a slot number, and storing the key in that slot. To find the key later, use the same rule again and look in that one slot.

You met this idea in the Maps lesson, where Java's HashMap did the hashing for you (see ). In this lesson, you build the table yourself, with simple whole-number keys such as ID numbers.

Hashing comes with a few words. The table below matches each one to the trays:

WordMeaningWith the trays
keythe value we store and later look forthe student ID
hash tablean array of slots where the keys are keptthe row of trays
mthe table size: how many slots there are10
slot (or bucket)one place in the table, numbered 0 to m − 1. "Bucket" is used when a slot can hold more than one key.one tray
hash function, h(k)the rule that turns a key k into a slot number"take the last digit"
collisiontwo different keys get the same slot number2025-0347 and 2025-0127 both go to tray 7

Where you see hashing in IT

  • Java's HashMap and HashSet (Sets and Maps lessons) are hash tables inside.
  • Databases can build a hash index, so finding a record by its ID does not mean reading the whole table.
  • A compiler keeps the names of your variables in a hash table, so it can look each one up quickly.

Good to know: you may also hear about "password hashing". It uses a special kind of hash function (a cryptographic one) that is built so nobody can work backward from the number to the password. Same idea of a fixed rule, different goal, and not part of this course.

Part 2 · The rule

Hash functions: k mod m

Hash function

A hash function is a fixed rule that takes a key and gives back a slot number from 0 to m − 1. We write it h(k): "h of k". The same key must always give the same number, or you could never find it again.

The most common rule for whole-number keys is the remainder after dividing by m. Mathematicians call the remainder "mod", and Java writes it :

h(k) = k mod m   (in Java: key % m)

Here is how to work it out by hand: divide, and keep only what is left over.

k mod mWorkingAnswer
37 mod 537 = 7 × 5 + 22
50 mod 750 = 7 × 7 + 11
700 mod 7700 = 100 × 7 + 00
4 mod 74 = 0 × 7 + 4 (smaller than m: the key itself)4

A remainder after dividing by m is always 0, 1, …, m − 1: exactly the slot numbers. That is why this rule fits a table of m slots so well. (The trays used it too: the last digit of a number is the number mod 10.)

Course convention

In this course, keys are whole numbers (0 or more) and the hash function is h(k) = k mod m, unless a question gives another one. Books call it the division method.

What makes a good hash function?

A good hash function has four properties:

  1. Same key, same slot, every time. Otherwise a stored key could never be found again.
  2. Quick to compute. The whole point is speed.
  3. Spreads the keys evenly over all the slots, so few keys collide.
  4. Uses the whole key, not just a part of it.

Property 4 is where the table size matters. A sari-sari store keeps its prices in a hash table with m = 10. Prices are often round numbers: ₱10, ₱20, ₱30, ₱40, ₱50. But k mod 10 is just the last digit, and every one of these ends in 0:

Keys: prices 10, 20, 30, 40, 50m = 10: k mod 1001234567891020304050All five land in slot 0. Slots 1 to 9 stay empty.m = 7 (a prime): k mod 705013021034405206Spread out: 10→3, 20→6, 30→2, 40→5, 50→1.
With m = 10, the rule only looks at the last digit. With m = 7, it depends on the whole number.

With m = 10, all five prices pile into slot 0, and finding one is as slow as searching a list. With a prime table size such as 7, 11, or 101, the remainder depends on the whole number, so keys that share a pattern still spread out. That is why books (and exams) like prime table sizes.

Good to know: Java's HashMap uses table sizes 16, 32, 64, … and finds the bucket with hashCode & (capacity − 1), as the Maps lesson showed. For sizes that are powers of 2, that gives the same answer as mod, only faster. To avoid the "only the last digits matter" problem, Java first mixes the hash code's high bits into its low bits.

Quick check: with m = 11, which slots do 25, 36, and 47 get?

25 = 2 × 11 + 3, 36 = 3 × 11 + 3, 47 = 4 × 11 + 3. All three get slot 3! Even a good rule cannot stop that. The next part explains what to do.

Part 3 · When keys clash

Collisions

Collision

A collision happens when two different keys get the same slot number: h(k1) = h(k2). Both keys must still be stored, and both must be found later.

Can a clever hash function avoid collisions completely? No. Student IDs can go from 0 to 99,999,999, but the registrar has only 10 trays. With more keys than slots, some keys must share. (This is the pigeonhole principle: put 11 letters into 10 pigeonholes, and at least one pigeonhole gets two.) So every hash table needs a plan for collisions.

There are two families of plans:

FamilyThe ideaIn real life
Separate chaining (Part 4)Each slot keeps a linked list (a chain) of all the keys that hash to it.Two forms in tray 7: one on top of the other.
Open addressing (Parts 5 and 6)Each slot holds one key. If the slot is taken, try other slots in a fixed order until one is free.Your parking slot is taken, so you try the next one.

To compare the methods fairly, every example in Parts 4 to 6 uses the same six keys and the same table size:

keys: 50, 700, 76, 85, 92, 73  ·  m = 7  ·  h(k) = k mod 7

Part 4 · Method 1

Separate chaining

Separate chaining

In separate chaining, every slot (called a bucket) holds a , called its chain, of all the keys that hash to it.
Insert k: compute h(k) and add k to that bucket's chain.
Search for k: compute h(k) and walk that one chain.

Course convention

A new key goes at the end of its chain, as in Java's HashMap. Some books add it at the front instead (no walk needed). Both are correct; follow the question if it says which.

Step through the six keys:

Separate chaining, by hand (m = 7)

Use Next to go through the steps.

Empty table, m = 7h(k) = k mod 70null1null2null3null4null5null6null

An empty table with 7 buckets (0 to 6). Every bucket starts as null: no chain yet. We insert the keys in order: 50, 700, 76, 85, 92, 73.

Step 1 of 8

On paper (and on exams), write each bucket with its chain:

0: 700
1: 50 → 85 → 92
2: 
3: 73
4: 
5: 
6: 76

Searching follows the same rule. To find 92: 92 mod 7 = 1, so walk bucket 1's chain: 50, 85, 92. Found after 3 comparisons. To find 15: 15 mod 7 = 1, walk 50, 85, 92, reach null: not found. Buckets 0, 2, 3, 4, 5, and 6 are never touched.

Mistake: stopping at the first node

Situation: a student searches for 92. They compute bucket 1, see 50 at the front, and say "not found".

Search for 92h(92) = 92 mod 7 = 1070015085922null3734null5null676stopped after 50: "not found" is wrong
The search looked only at the first node of bucket 1.

What went wrong: a bucket can hold many keys. Seeing a different key at the front means "keep walking", not "not found".

Fix: walk the whole chain until you find the key or reach null.

Separate chaining in Java

The table is an array of chain heads: Node[] table = new Node[m];. Each Node holds a key and next, like the nodes in the Linked lists lesson. Every bucket starts as null (no chain yet). Step through insert(92) when bucket 1 already holds 50 and 85:

Walkthrough: insert(92)

Use Next to go step by step, or tap a line to jump to it.

insert(92)i = 92 % 7 = 10700150852null3null4null5null676

key = 92

The table already holds 700, 50, 76 and 85 (bucket 1 has a chain of two). We call insert(92).

Step 1 of 8
 1void insert(int key) { 2  int i = key % m;               // which bucket? 3  Node newNode = new Node(key); 4  if (table[i] == null) {        // empty bucket 5    table[i] = newNode; 6    return; 7  } 8  Node current = table[i]; 9  while (current.next != null) { // walk to the last node10    current = current.next;11  }12  current.next = newNode;        // add at the end of the chain13}

Here is how to read the key lines aloud:

CodeRead it as
int i = key % m;"the bucket number is the remainder of key divided by m."
if (table[i] == null)"if bucket i has no chain yet..."
while (current.next != null)"while current is not the last node..."
current.next = newNode;"link the new node after the last one."

Part 5 · Method 2

Open addressing: linear probing

Chaining needs a node, and memory for its link, for every key. Open addressing keeps everything inside the array itself: one key per slot, no chains. When a slot is taken, it looks for another one.

Think of numbered parking

Each car's number decides its parking slot. Car 92's slot is 92 mod 7 = 1. Slot 1 is taken, so the driver tries slot 2: taken too. Slot 3 is free, so the car parks there. Anyone looking for car 92 later starts at slot 1 and walks the same way.

Parking by numberCar 92's slot: 92 mod 7 = 101234567005085769292✗✗free!Slots 1 and 2 are taken. Slot 3 is free: park there.Taken? Try the next slot. That is linear probing.
Taken? Try the next slot.

Open addressing and probing

In open addressing, every key is stored in the table itself, at most one key per slot. On a collision, the table tries other slots in a fixed order until it finds a free one. Each try is called a probe, and the order of tries is the probe sequence.

Linear probing

In linear probing, try number i goes to slot (h(k) + i) mod m, for i = 0, 1, 2, …
So the tries are h(k), then the next slot, then the next, one by one. Try i = 0 is the key's home slot, h(k).

Why "mod m" again? If the tries pass the last slot (m − 1), mod m brings them back around to slot 0, like a clock. Step through the same six keys:

Linear probing, by hand (m = 7)

Use Next to go through the steps.

Empty table, m = 7h(k) = k mod 7slot = (h + i) mod 70123456

An empty table with 7 slots (0 to 6). Each slot holds at most one key. We insert 50, 700, 76, 85, 92, 73 in order, with linear probing.

Step 1 of 8

On paper, list every key's tries. The number badges in the pictures are the try numbers i:

Keyh(k) = k mod 7Slots triedGoes into
50111
700000
76666
8511, 22
9211, 2, 33
7333, 44

Clusters

Look at the final table: slots 0 to 4 are one solid block. Any new key whose home slot is anywhere in that block has to walk to the end of it, and then it makes the block longer. Try 69: 69 mod 7 = 6, and slot 6 is taken, so it wraps around to 0, 1, 2, 3, 4 (all taken) and lands in slot 5 after 7 tries.

These blocks are called clusters (books say primary clustering). They are linear probing's big weakness, and the reason for the methods in Part 6.

Searching: follow the same path

A search must take the same tries the insert took. It stops in one of two ways: it finds the key, or it reaches an empty slot. An empty slot means the key was never inserted: if it had been, it would have stopped there at the latest.

In Java, the table is an int[]. Since keys are 0 or more, the value -1 can stand for "empty" (named EMPTY). Step through find(92), then find(15):

Walkthrough: find(92) and find(15)

Use Next to go step by step, or tap a line to jump to it.

find(92)home = 92 % 7 = 1slot = (home + i) % 707001502853924735676

key = 92

This is the table we built with linear probing. We call find(92). The method gives back the slot where 92 is, or -1 (like the searches in the Searching lesson).

Step 1 of 8
 1int find(int key) { 2  int home = key % m; 3  for (int i = 0; i < m; i++) { 4    int slot = (home + i) % m;          // linear probing 5    if (table[slot] == EMPTY) { 6      return -1;                        // a never-used slot: stop 7    } 8    if (table[slot] == key) { 9      return slot;                      // found it10    }11  }12  return -1;                            // tried every slot13}

Here is how to read the key lines aloud:

CodeRead it as
int slot = (home + i) % m;"try number i: i slots after home, wrapping around past the end."
if (table[slot] == EMPTY)"if this slot was never used, the key cannot be further along: stop."
return slot;"found: give back the slot number."

Deleting: leave a mark

Deleting from a chain is easy: unlink the node. In open addressing, it is tricky, because other keys may have probed past the slot you want to empty. Step through deleting 85:

Deleting with linear probing

Use Next to go through the steps.

find(92)h(92) = 92 mod 7 = 1slot = (h + i) mod 707001502853924735676i=0: (1+0) mod 7 = 1no0i=1: (1+1) mod 7 = 2no1i=2: (1+2) mod 7 = 3found2found at slot 3 after 3 tries

First, a normal search. 92 belongs in slot 1, but when it was inserted, slots 1 and 2 were taken, so it moved on to slot 3. A search must follow the same path: slot 1 (50, not it), slot 2 (85, not it), slot 3: found.

Step 1 of 5

Deleted mark (tombstone)

When a key is removed in open addressing, its slot gets a deleted mark instead of becoming empty. A search treats the mark as "something was here: keep going". An insert may reuse the slot as a free one.

Both tables in one program

Here is the chaining table and the linear-probing table in one runnable file, with insert, search, remove, and print. Run it and match the output to the pictures above:

 1class Node {
 2  int key;
 3  Node next;
 4
 5  Node(int key) {
 6    this.key = key;
 7  }
 8}
 9
10class ChainTable {
11  Node[] table;
12  int m;
13
14  ChainTable(int m) {
15    this.m = m;
16    table = new Node[m];           // every bucket starts as null
17  }
18
19  void insert(int key) {
20    int i = key % m;               // which bucket?
21    Node newNode = new Node(key);
22    if (table[i] == null) {        // empty bucket
23      table[i] = newNode;
24      return;
25    }
26    Node current = table[i];
27    while (current.next != null) { // walk to the last node
28      current = current.next;
29    }
30    current.next = newNode;        // add at the end of the chain
31  }
32
33  boolean contains(int key) {
34    Node current = table[key % m]; // only this bucket's chain
35    while (current != null) {
36      if (current.key == key) {
37        return true;
38      }
39      current = current.next;
40    }
41    return false;
42  }
43
44  void print() {
45    for (int i = 0; i < m; i++) {
46      System.out.print(i + ":");
47      Node current = table[i];
48      while (current != null) {
49        System.out.print(" " + current.key);
50        current = current.next;
51      }
52      System.out.println();
53    }
54  }
55}
56
57class ProbeTable {
58  static final int EMPTY = -1;     // never used
59  static final int DELETED = -2;   // a key was removed here
60  int[] table;
61  int m;
62
63  ProbeTable(int m) {
64    this.m = m;
65    table = new int[m];
66    for (int i = 0; i < m; i++) {
67      table[i] = EMPTY;
68    }
69  }
70
71  boolean insert(int key) {
72    int home = key % m;
73    for (int i = 0; i < m; i++) {
74      int slot = (home + i) % m;   // linear probing
75      if (table[slot] == EMPTY || table[slot] == DELETED) {
76        table[slot] = key;
77        return true;
78      }
79    }
80    return false;                  // no free slot
81  }
82
83  int find(int key) {
84    int home = key % m;
85    for (int i = 0; i < m; i++) {
86      int slot = (home + i) % m;          // linear probing
87      if (table[slot] == EMPTY) {
88        return -1;                        // a never-used slot: stop
89      }
90      if (table[slot] == key) {
91        return slot;                      // found it
92      }
93    }
94    return -1;                            // tried every slot
95  }
96
97  void remove(int key) {
98    int slot = find(key);
99    if (slot != -1) {
100      table[slot] = DELETED;       // a mark, not EMPTY
101    }
102  }
103
104  void print() {
105    for (int i = 0; i < m; i++) {
106      if (table[i] == EMPTY) {
107        System.out.println(i + ": -");
108      } else if (table[i] == DELETED) {
109        System.out.println(i + ": deleted");
110      } else {
111        System.out.println(i + ": " + table[i]);
112      }
113    }
114  }
115}
116
117public class Main {
118  public static void main(String[] args) {
119    int[] keys = {50, 700, 76, 85, 92, 73};
120
121    ChainTable chain = new ChainTable(7);
122    for (int i = 0; i < keys.length; i++) {
123      chain.insert(keys[i]);
124    }
125    chain.print();
126    System.out.println(chain.contains(92));   // true
127    System.out.println(chain.contains(15));   // false
128
129    ProbeTable probe = new ProbeTable(7);
130    for (int i = 0; i < keys.length; i++) {
131      probe.insert(keys[i]);
132    }
133    probe.print();
134    System.out.println(probe.find(92));       // 3
135    probe.remove(85);
136    System.out.println(probe.find(92));       // still 3
137    System.out.println(probe.find(85));       // -1
138  }
139}

▶ This program runs as is. Try it in the Programiz online compiler: tap Copy, paste it there, and press Run.

Output:

0: 700
1: 50 85 92
2:
3: 73
4:
5:
6: 76
true
false
0: 700
1: 50
2: 85
3: 92
4: 73
5: -
6: 76
3
3
-1

Two new lines to read aloud:

CodeRead it as
static final int EMPTY = -1;"EMPTY is a constant (a fixed value with a name) equal to -1." final means it can never change. Keys are 0 or more, so -1 can never be a real key.
table[slot] == EMPTY || table[slot] == DELETED"the slot was never used, or its key was removed": either way, insert may use it.

Good to know: these tables assume every key is different, like student IDs. A real table would first search for the key and skip it if it is already there. Negative keys need Math.floorMod(key, m), because Java's % gives a negative remainder for a negative number.

Part 6 · Methods 3 and 4

Quadratic probing and double hashing

Linear probing's trouble is that colliding keys all walk the same path, one slot at a time, so they pile up into clusters. The fix: jump farther on each try. Two ways to do that:

Quadratic probing

Quadratic probing

In quadratic probing, try number i goes to slot (h(k) + i²) mod m.
The jumps from home grow: 0, 1, 4, 9, 16, … slots away ("quadratic" means "with a square").

Same keys, same table:

Quadratic probing, by hand (m = 7)

Use Next to go through the steps.

Empty table, m = 7h(k) = k mod 7slot = (h + i²) mod 70123456

An empty table with 7 slots (0 to 6). Each slot holds at most one key. We insert 50, 700, 76, 85, 92, 73 in order, with quadratic probing.

Step 1 of 8
Keyh(k)Slots triedGoes into
50111
700000
76666
8511, 22
9211, 2, 55
73333

See 92: its third try jumped from slot 1 straight to slot 5 (1 + 2² = 5), out of the crowd.

Watch out: quadratic probing can miss free slots

Because the jumps skip slots, the tries can keep landing on the same few taken slots while another slot is still free. In the lab in Part 7, tap all the same h with Quadratic: 18 and 23 cannot be placed, even though slots 0 and 1 are empty. (With Double, all five fit.)

The safe rule: use a prime m and keep the table at most half full. Then quadratic probing always finds a free slot.

Double hashing

Double hashing

In double hashing, a second hash function gives each key its own jump size, its step. Try number i goes to slot (h(k) + i × step(k)) mod m.
Two keys with the same home slot usually have different steps, so they stop following each other.

Course convention

The step is step(k) = R − (k mod R), where R is the largest prime smaller than m (m = 7 → R = 5; m = 5 → R = 3). It is always between 1 and R, so it is never 0: a step of 0 would try the same slot forever. Books use different second functions, so on a quiz or exam, always use the one the question gives.

With m = 7, R = 5. Same keys again:

Double hashing, by hand (m = 7, R = 5)

Use Next to go through the steps.

Empty table, m = 7h(k) = k mod 7step(k) = 5 − (k mod 5)0123456

An empty table with 7 slots (0 to 6). Each slot holds at most one key. We insert 50, 700, 76, 85, 92, 73 in order, with double hashing.

Step 1 of 8
Keyh(k)step(k) = 5 − (k mod 5)Slots triedGoes into
501511
7000500
766466
85151, 6, 44
92131, 4, 0, 33
73323, 55

The three results side by side

Keys 50, 700, 76, 85, 92, 73 · m = 7Linear0700150285392473567610 triesQuadratic070015028537345926769 triesDouble0700150239248557367612 tries
Same keys, same m, three probing rules, three different tables.

In this tiny example, double hashing needed the most tries. With only 7 slots and 6 keys, every method runs into full slots. The payoff of quadratic probing and double hashing shows in big tables, where linear probing's clusters keep growing and the other two scatter the keys.

Here is a summary of the four methods:

MethodTry i goes toGoodWatch out
Separate chainingalways bucket h(k) (add to its chain)simple; never "full"; easy deleteextra memory for nodes and links
Linear probing(h + i) mod msimple; no extra memoryclusters
Quadratic probing(h + i²) mod mbreaks up clusterscan miss free slots (use prime m, at most half full)
Double hashing(h + i × step(k)) mod meach key has its own pathtwo hash functions to compute; step must never be 0

Part 7 · Try it

The hash table lab

Put any keys into a table with any of the four methods, one key per step. Things to try:

  • The lesson keys with all four methods: compare the "tries in all".
  • wrap around with Linear: watch 17 go past slot 4 and back to slot 0.
  • prices (m = 10), then the same keys with m = 7 or 11.
  • all the same h with Quadratic, then with Double.

Hash table lab

Tap a method to insert the keys. Use Next step to watch one key at a time. Change m or the keys and the lab runs again.

m =

Quick tries:

Step 1 of 1
Color key
home slot or bucket (h) tried, but taken where the key goes 1 try number i

Part 8 · When the table fills up

Load factor and rehashing

The more keys a table holds, the more collisions there are, and the more tries each insert and search needs. To know when a table is getting too crowded, we measure how full it is.

Load factor

The load factor is α = n / m ("alpha"): n is the number of keys stored, and m is the number of slots. Our lesson table has 6 keys in 7 slots: α = 6/7 ≈ 0.86.

It means slightly different things for the two families:

  • Separate chaining: α is the average chain length. It can go above 1 (more keys than buckets), but long chains are slow.
  • Open addressing: α can never go above 1, because each slot holds one key. At α = 1 the table is full.

For linear probing in a big table, here is about how many tries a search takes on average (from the formulas in Sedgewick and Wayne's Algorithms):

Load factor αSearch, key is thereSearch, key is not there
0.5 (half full)1.5 tries2.5 tries
0.752.5 tries8.5 tries
0.95.5 tries50.5 tries

At half full, it is still about one or two tries. Near full, it gets slow fast. So tables are kept from getting too full: open addressing usually aims for α ≤ 0.5, and Java's HashMap (chaining) grows when α passes 0.75.

Rehashing

Rehashing means making a bigger table (often about twice as big, and a prime) and inserting every key again with the new m.

Why not just copy the keys over to the same slot numbers? Because every slot number came from k mod m, and m has changed. In the bigger table, the keys belong in new places:

Before: m = 5load 4/5 = 0.8After: m = 11load 4/11 ≈ 0.36017121232743303311223452761778910insert everykey again12→2, 27→3 (2 taken),33→4 (3 taken),17→0 (2, 3, 4 taken)New m, so new slots: 12→1, 27→5, 33→0, 17→6.No collisions now.
Rehashing 4 keys from m = 5 into m = 11 (linear probing in both).

Rehashing takes work (every key is inserted again), but it happens rarely: after doubling, many more inserts fit before the next one. So, on average, inserts stay fast.

Part 9 · Why this matters

How fast is a hash table?

Here is how finding one key compares, for n keys:

Where the keys areFind, usually (average case)Find, worst caseKeeps keys in order?
Unsorted array (linear search)up to n checksnno
Sorted array (binary search)about log₂ n (20 for a million)about log₂ nyes
Balanced BST (AVL, red-black)about log₂ nabout log₂ nyes (in-order)
Hash table1 or 2 tries, if the load factor is kept lownno

Books call the "usually" column the average case: ordinary keys, a good hash function, and a low load factor. The worst case happens when (almost) every key collides: one long chain, or one long cluster. Then a search is as slow as linear search: with 1,000 keys all in one bucket, it may check all 1,000. Remember . A good hash function, a prime m, and rehashing keep that from happening in practice.

Notice the last column: a hash table scatters the keys on purpose, so it cannot list them in order, or quickly find the smallest. Here is how to choose:

You need to...Good choice
find, add, and remove by exact key, many times (ID → student record, username → account)hash table (HashMap, HashSet)
keep keys sorted, find the smallest or largest, or list a range (all grades from 80 to 90)balanced BST (TreeMap, TreeSet)
store a few values, or values by positionarray or list

Java's own HashMap and HashSet use separate chaining (see ), and double their table size when the load factor passes 0.75.

Good to know: books write the hash table's usual speed as O(1) on average ("constant": it does not grow with n), and its worst case as O(n). You will meet these names again when the course analyzes algorithms.

Part 10 · Your turn

Practice

On paper

  1. m = 5, h(k) = k mod 5. Insert the keys 12, 27, 33, 17, 40, in this order, using (a) separate chaining, (b) linear probing, (c) quadratic probing, (d) double hashing with step(k) = 3 − (k mod 3). For (b) to (d), write a table of tries like the ones in Parts 5 and 6. The first row of (b) is done for you:
    Keyh(k)Slots triedGoes into
    12222
  2. Use your linear-probing table from 1(b). (a) Which slots does a search for 40 check? (b) Which slots does a search for 22 check, and what does it return?
  3. (a) What is the load factor of the table in 1(b)? (b) Rehash its keys into a table with m = 11, using linear probing.
  4. Spot the bug. This program should insert four keys and print "done". What happens instead, and how do you fix it?
     1public class Main {
     2  static int m = 5;
     3  static int[] table = {-1, -1, -1, -1, -1};
     4
     5  static void insert(int key) {
     6    int home = key % m;
     7    for (int i = 0; i < m; i++) {
     8      int slot = home + i;
     9      if (table[slot] == -1) {
    10        table[slot] = key;
    11        return;
    12      }
    13    }
    14  }
    15
    16  public static void main(String[] args) {
    17    insert(12);
    18    insert(27);
    19    insert(33);
    20    insert(17);
    21    System.out.println("done");
    22  }
    23}
  5. Hash table or not? (a) A canteen card system looks up a student's balance by card number, thousands of times a day. (b) The registrar must print the Dean's List from highest grade to lowest. (c) A linear-probing table is 90% full and searches are getting slow.
Show the answers

For self-study, not for grading. All tables were checked by running the code in Java.

  1. (a) Separate chaining:
    0: 40
    1: 
    2: 12 → 27 → 17
    3: 33
    4: 
    (b) Linear probing:
    Keyh(k)Slots triedGoes into
    12222
    2722, 33
    3333, 44
    1722, 3, 4, 00
    4000, 11
    0: 17
    1: 40
    2: 12
    3: 27
    4: 33
    17 wraps around: slots 2, 3, 4 are taken, so (2 + 3) mod 5 = 0.
    (c) Quadratic probing:
    Keyh(k)Slots triedGoes into
    12222
    2722, 33
    3333, 44
    1722, 3, 11
    40000
    0: 40
    1: 17
    2: 12
    3: 27
    4: 33
    17: (2 + 0) = 2 taken, (2 + 1) = 3 taken, (2 + 4) mod 5 = 1 free.
    (d) Double hashing, step(k) = 3 − (k mod 3):
    Keyh(k)step(k)Slots triedGoes into
    122322
    27232, 00
    333333
    17212, 3, 44
    40020, 2, 4, 11
    0: 27
    1: 40
    2: 12
    3: 33
    4: 17
  2. (a) 40 mod 5 = 0: slot 0 (17, not it), slot 1 (40): found after 2 tries. (b) 22 mod 5 = 2: slots 2, 3, 4, 0, 1 all hold other keys and none is empty, so the loop tries all 5 slots and returns -1. A full table makes every "not found" search check every slot.
  3. (a) α = 5 / 5 = 1.0: completely full. (b) With mod 11:
    0: 33
    1: 12
    5: 27
    6: 17
    7: 40
    All the other slots are empty, and no key collides.
  4. It crashes while inserting 17: ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5. 17 mod 5 = 2; slots 2, 3, and 4 are taken, so the next try is slot 2 + 3 = 5, which does not exist. Line 8 forgot to wrap around. Fix: int slot = (home + i) % m;.
  5. (a) Hash table: many lookups by exact key. (b) Not a hash table: it has no order. Use a sorted structure (a BST or TreeMap), or sort the grades. (c) Rehash into a bigger table (about twice the size, a prime), so the load factor drops below 0.5.

Your coding task: quadratic probing

This small table uses an array of 5 slots, with -1 for empty. Finish insert so it uses quadratic probing: write about 5 lines where it says YOUR CODE, then run it. It should print the same table as your answer to Problem 1(c), then false (the table is full, so 99 cannot go in).

 1public class Main {
 2  static int m = 5;
 3  static int[] table = {-1, -1, -1, -1, -1};   // -1 = empty
 4
 5  static boolean insert(int key) {
 6    int home = key % m;
 7    for (int i = 0; i < m; i++) {
 8      // YOUR CODE: work out the slot with QUADRATIC probing,
 9      // and if that slot is empty (-1), store the key there
10      // and return true.
11
12    }
13    return false;                    // no free slot found
14  }
15
16  public static void main(String[] args) {
17    int[] keys = {12, 27, 33, 17, 40};
18    for (int i = 0; i < keys.length; i++) {
19      insert(keys[i]);
20    }
21    for (int i = 0; i < m; i++) {
22      System.out.println(i + ": " + table[i]);
23    }
24    System.out.println(insert(99));
25  }
26}

▶ This program runs as is. Try it in the Programiz online compiler: tap Copy, paste it there, and press Run.

Show one possible answer
 1static boolean insert(int key) {
 2  int home = key % m;
 3  for (int i = 0; i < m; i++) {
 4    int slot = (home + i * i) % m;   // quadratic probing
 5    if (table[slot] == -1) {
 6      table[slot] = key;
 7      return true;
 8    }
 9  }
10  return false;                    // no free slot found
11}

Output:

0: 40
1: 17
2: 12
3: 27
4: 33
false

The only change from linear probing is i * i instead of i. Java has no "squared" operator, so i² is written i * i.

Part 11 · Check yourself

Self-check

Not graded. Tap an answer to see if you got it, and read why.

  1. What is the main job of a hash function?

  2. What is a collision?

  3. m = 7 and h(k) = k mod 7. Which slot does the key 45 get?

  4. Linear probing, m = 7. Slots 3 and 4 are taken; slot 5 is free. Where does the key 10 go?

  5. Quadratic probing, m = 11, h(k) = 2. Which slot is try i = 2?

  6. In open addressing, why is a deleted key replaced with a "deleted" mark instead of an empty slot?

  7. A table has m = 10 slots and holds 8 keys. What is its load factor?

  8. Why is a prime number (like 7, 11, 101) a good table size for h(k) = k mod m?

  9. A chaining table holds 1,000 keys, and a bad hash function put all of them in bucket 0. In the worst case, how many keys does a search check?

Score: 0 / 9

Key takeaways

  1. Hashing turns a key into a slot number with a fixed rule, so a search can jump straight to one slot.
  2. In this course, h(k) = k mod m. A prime m spreads keys better.
  3. Collisions cannot be avoided. Separate chaining keeps a chain per bucket; open addressing tries other slots.
  4. Try i: linear (h + i) mod m; quadratic (h + i²) mod m; double hashing (h + i × step(k)) mod m.
  5. Searches follow the insert's path and stop at the key or at an empty slot, so deletes leave a "deleted" mark.
  6. Load factor α = n / m. Keep it low, and rehash into a bigger table when it gets high. Then finding a key takes about 1 or 2 tries on average; in the worst case (every key collides) it takes up to n. A hash table keeps no order.

Next: Algorithm complexity analysis, the last lesson of the course (coming soon). It brings together the Big-O names you met in searching, sorting, and hashing. The Maps lesson shows how Java's HashMap turns String keys into numbers first.

References

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to Algorithms, 4th ed. MIT Press, 2022, Chapter 11 (Hash tables; the division method; open addressing).
  2. R. Sedgewick and K. Wayne, Algorithms, 4th ed. Addison-Wesley, 2011, Section 3.4 (hash tables; average tries for linear probing). algs4.cs.princeton.edu
  3. M. T. Goodrich, R. Tamassia, and M. H. Goldwasser, Data Structures and Algorithms in Java, 6th ed. Wiley, 2014, Section 10.2 (double hashing with q − (k mod q)).
  4. Programiz, "Hash Table." programiz.com/dsa/hash-table
  5. Oracle, "HashMap (Java SE 21 & JDK 21)," Java API documentation (load factor 0.75). docs.oracle.com
  6. ITC 120 Course Syllabus (Hashing Algorithms), Davao Oriental State University.