Direct Access Table
Take huge array and use phone numbers as index in the array. if phone
number is not present entry is empty, else the array entry stores
pointer to records corresponding to phone number.
Searching in DA: O(1) but HUGE Extra Space required.
Hash Table is improvement over DAT
Hash Table?
This is a data structure which can search/delete element in almost O(1)
time, Unlike arrays or linked lists that require searching through
elements one by one and takes O(n) time
Complexity: Average=O(1), Worst case=O(n)
Disadvantages:
1. Elements are not sorted
2. Rehashing: Once all HT entries are filled it needs
resized/rehashing which is a time-consuming operation. Let HT size =
100, we want to insert 101st element. Not only the size of hash table is
enlarged to 150, all element in hash table have to be rehashed. This
insertion operation takes O(n).
Hash Function? Hash function maps a big number or string to a
small integer that can be used as index in hash table.
key -> |Hash Function| -> index of array/table
Internal Implementation of Hash Table
Let use consider simple example to insert following key,value pairs:
{"a",1}, {"b",2}, {"b",3}
Keys("a", "b", "b") are passed to hash function and indexes are returned
in bucket which acts as hash table
Collision: if same index is returned, chaining is done, ie
element is inserted into next position in vector
| Hash table |
Let these need to be inserted into hash table: {a,1}, {b,2}, {c,3}, {d,4}
Bucket index array is contiguous in memoryActual key-value pairs are scattered across the heap. |
|
Hash Collision
This means hash function provides same index for 2 different keys.
Solutions to Hash Collision
1. Open Addressing
| Type of Open Addressing | What | Example |
|---|---|---|
| 1. Linear Probing |
if the target slot is occupied, the table checks the next
sequential slot (index + 1, wrapping around if necessary) until an empty slot is found. |
|
| 2. Quadratic Probing |
Uses a quadratic function to find the next slot. ax2 + bx + c |
|
| 3. Double Hashing | Uses double hash function to re-calculate the index. hash1(x) = (hash1(x) + i*hash2(x))%hash_table_size |
2. Seperate Chaining (Discussed above)
Extra Overhead with hash table
Memory Overhead:
1. Every bucket requires memory for a pointer. Since key-value is
stored in Linked List, requires extra bytes to store a pointer to the
"next" node in the chain.
2. Rehashing Overhead. When
Load Factor becomes greater than .75, then
larger bucket is allocated, all keys are rehashed
Computation Overhead
1. Every read, write, and delete requires running the hash
function algorithm to calculate the index.
Load Factor
Load factor measures of how "full" the hash table is
Load factor = Total number of elements / number of slots in buckets
Above example:
elements: (a,1), (b,2), (c,3), and (d,4)
slots: 3
LF = 4/3 = 1.33
if (Load Factor > 0.75), then rehashing is triggered.
(Load Factor > 1) means
- Elements > slots, that means there is atleast 1 collision
- Collision means Search is not O(1) now, its moving towards O(n)
C++ hashmap(unordered_map / unordered_set) = Seperate Chaining
std::unordered_map or std::unordered_set is implemented using separate
chaining for collision resolution.
Allocations:
Similar to vector
A default-constructed hash table (unordered_map <int,
string> map;) starts with a bucket count of 0.
1st map.insert(), it allocates its initial bucket array
(size=1).
Rehashing
Rehashing / Resizing
When Load Factor > 0.75, a rehash is
triggered automatically.
During a rehash, a new, l arger bucket array is allocated (typically
scaling to the next prime number), and all
existing elements are re-hashed and redistributed into the new
buckets.
Let us consider unordered_map<int,string> storing unique keys. At
start of program sizeof hash table=3
Key | Value
----------
01 | amit
02 | never
03 | give
Now, (4, up) need to be stored, but hash table has no space so size of
hash table is increased to 6. (old Hash function = %3) we can only goto
index number=2.
But we want to reach 5. Hence Hash function is changed (new Hash
function = %6). So hash is again calculated for existing values.
Implementations
2-left hashing
2 equal sized hash tables having 2 seperate hash functions are used.
Table1(hash1), Table2(hash2). Eg: Cuckoo Hashing
Inserting key into table:
Always keep Table1 loaded, key is inserted into Table2 only when too
many collisions happen on Table1.
Advantages? 1. On parallel systems. 2 cores can query 2 hash
tables, ie Thread-1 can query Table1 and Thread2 Table2.
d-left Hashing
Split 1 hash table into d blocks. Similar to 2-left hashing, left most
hashtable should be loaded.
d buckets, n entries. Each bucket has n/d entries
Code
-
1. Student Hash table
Hash table stores Student data (Key:Value)
(1,20) (2,70) (42,80) (4,25) (12,44) (13,78) (14,32) (17,11) (37,97)
2. Leetcode Solved. Design a HashMap without using any built-in hash
table libraries.