Problem. (Dictionary) A dictionary is a mapping from distinct keys to values; equivalently, it's a collection of objects {xi} such that each object xi has attributes xi.Key and xi.Value. Our dictionary must support:
Insert(k,v): Adds the key-value pair (k,v).
Delete(k): Removes the item x satisfying x.Key=k (if it exists).
Search(k): Returns the value x.Value for the x satisfying x.Key=k (or returns Null if no x exists).
Let U denote the universe of all possible keys, and let n denote the number of objects in the dictionary.
We want ∣U∣≫n, of course. For simplicity, in this lecture we'll assume U⊆Z≥0.
Solution. (Solution #1: Huge Array) Just allocate a huge array of size ∣U∣. This supports every operation in Θ(1) time, but requires Θ(∣U∣) space, which is terribly inefficient.
Solution. (Solution #2: Doubly-Linked List) Just store everything in a doubly-linked list. This requires only Θ(n) space, but takes Θ(n) time for all three operations.
Solution. (Solution #3: Balanced Binary Tree) Solution #2, except every operation only takes Θ(logn) time!
To do even better, we'll need to use hash functions.
Definition (Hash Function). We seek to reduce the universe size by building a hash function h:U→{0,1,…,m−1}. This gives us the time complexity of Solution #1, but the space complexity of Solution #2!
Here, say m is the size of the hash table, and say α:=mn is its load factor. We have the following concern:
Problem. (Injectivity) Evidently ∣U∣≫m, so h is not injective. What if two keys have the same image?
Solution. (Hashing & Chaining) Each of {0,1,…,m−1} maintains a doubly-linked list. Every (k,v) is appended to the head of the doubly-linked list of h(k).
Then Insert(k,v) takes Θ(1) time, and our space complexity is Θ(m+n). Meanwhile,
In the best case, Delete(k) and Search(k) take Θ(mm+n)=Θ(1+mn)=Θ(1+α) time complexity.
In the worst case, Delete(k) and Search(k) take Θ(n) time complexity.
To be explicit: the cost of Delete(k) and Search(k) depends on the length of the doubly-linked list of h(k).
More generally, any deterministic hash function will fail in the worst case. But random hash functions…
Problem. (Randomization Goal) Consider any worst-case set of keys k1,k2,…,kn∈U. Still, then, we have:
Pr[operations k1,k2,…,kn all take Θ(1+α)]≥0.999.
Note that Pr is taken over the hash function randomization, not the choice of keys.
To check your understanding… note that we don't seek, more strongly, this instead:
Pr[∀k1,k2,…,kn, the operations k1,k2,…,kn all take Θ(1+α)]≥0.999.
It's because the event described above is literally impossible, for reasons explained earlier.
Problem. (Simpler Goal) Instead, we only ask that E[running time of any operation]≤O(1+α).
Proving this simpler goal does not imply the original goal, but the proof idea is similar.
Claim. Uniform random hashing—i.e. drawing h uniformly from all functions U→{0,1,…,m−1}—satisfies the simpler goal.
Proof: Just linearity of expectation. ■
Except it's not really the end. How, exactly, do we implement h itself? Note that h is, itself, a dictionary!
Definition. Call a family of hash functions H⊆{all hash functions h:U→{0,1,…,m−1}}uniform if:
h∼HPr[h(k)=ℓ]=m1,∀k∈U,∀0≤ℓ≤m−1
Uniform hash families are not good enough. For example, the uniform family {hℓ:k↦ℓ}ℓ=0m−1 is useless.
Definition. Consider some arbitrary sequence of keys k1,…,kn∈U. Then for each j=1,2,…,n, we define:
Cj:=#{j′=j∣h(kj′)=h(kj)}.
Note that Search(kj) and Delete(kj) both take O(1+Cj) time.
Claim. Uniform random hashing satisfies E[Cj]≤α for all j=1,2,…,n.
Proof: Just linearity of expectation. ■
Definition. Call a family of hash functions H⊆{all hash functions h:U→{0,1,…,m−1}}universal if:
∀k1=k2,h∼HPr[h(k1)=h(k2)]≤m1.
Claim. Any universal hash family satisfies E[Cj]≤α for all j=1,2,…,n.
By corollary, any universal hash family achieves our goal of E[running time of any operation]≤O(1+α).
Proof: Just LoE again. We've generalized uniform random hashing in a way that preserves the proof. ■
Problem. (Universal Hash Family) How can we define a universal hash family?
Solution. (Universal Hash Family: Failures) We might try the following things:
Failure #1. Just take h(k):=k%m.
This doesn't work. It's deterministic and not even a family.