CITS2200 - Lecture 10
Hash Tables - Slides
Recap: CITS2200 - Lecture 9
Issue with Lists
- Lists can be used to implement various data structures
- e.g. maps and sets
- However, they take to search:
- The item you search for may or may not exist
- Thus, we must check every entry in the list
- Worst case → time to find object
- Sorting a list may make searching more efficient, but:
- Inserting an object takes time
- Which makes things equally as inefficient
Hash Functions and Hash Tables
- Hash function
hmaps keys to integers in a fixed range[0, N-1], whereNis the size of the table (array)- Goal: store item
(k, v)at indexh(k)in the array - Example:
h(x) = x mod Nfor integer keys
- Goal: store item
- Hash table for a given key type consists of:
- Hash function
h - Array (called table) of size
N
- Hash function
Hash function is typically composed of two steps:
- Hash code : keys → integers
- Compression function : integers →
[0, N-1]
Goal of hash function → “disperse” keys in an apparently random way
Compression Functions
Two common approaches:
- Division:
y mod NN→ size of hash table, usually prime
- MAD (Multiply, Add and Divide):
(ay + b) mod Naandbare non-negative integersa mod N- Otherwise, everything would map to the same value
b
- Otherwise, everything would map to the same value
Collisions Handling
mod ensures for any input, output is between 0 and N-1
x mod yis equal to the remainder of
Using mod, or even other functions, it becomes inevitable that some values would give the same output
- These are known as collisions
Collisions can be handled in several ways:
Separate Chaining
- Each cell in table points to a linked list of all entries that map there
- If a collision occurs, the new item is just appended to that cell’s list
Index 1 → [025-612-0001]
Index 4 → [451-229-0004] → [981-101-0004]
Operations just delegate to the list at A[h(k)]:
get(k): returnA[h(k)].get(k)put(k,v): callA[h(k)].put(k,v), increment count if new keyremove(k): callA[h(k)].remove(k), decrement count if found
Downside: Requires extra memory outside the table for the linked lists
Open Addressing (Linear Probing)
- No chaining, keep everything inside table
- On collision, find next available cell
Each position checked = a probe
- Colliding items lump together, causing future collisions to cause a longer sequence of probes known as primary clustering
Searching with Linear Probing (get(k))
- Start at
h(k), probe consecutively until:- Item with key
kis found → return it - Empty cell found → key not in table
- All
Ncells probed → key not in table
- Item with key
Deleting items from Table:
- We cannot just delete an item from the table
- Would break the probe chain for future searches
- Instead, replace deleted items with a special
AVAILABLEobject
remove(k): findk, replace withAVAILABLE, return valueput(k,v): treatAVAILABLEcells as valid insertion spots
Double Hashing
- Use secondary hash function
d(k)to determine step size:- Probe sequence:
(h(k) + j·d(k)) mod Nforj = 0, 1, 2, ...Nmust be prime to allow probing of all cells
- Common secondary function:
d(k) = q - (k mod q)q < Nandqis primed(k)must never be zero (infinite loop)
- Probe sequence:
- @ Spreads items more evenly than linear probing since the step size varies per key
Performance of Hashing
| Scenario | Complexity |
|---|---|
| Worst case (all keys collide) | |
| Expected case (good hash fn) |
The load factor α = n/N affects performance of a hash table
- Expected probes for open addressing =
1 / (1 - α) αis usually well below 100%- Hashing is very fast in practice