Hash Table Concept and Hash Functions in DSA Rust - Time & Space Complexity
We want to understand how fast a hash table can find or store data.
The question is: how does the time to find or add items grow as we add more items?
Analyze the time complexity of the following code snippet.
struct HashTable {
buckets: Vec>,
}
impl HashTable {
fn hash(&self, key: &str) -> usize {
key.bytes().fold(0, |acc, b| acc.wrapping_add(b as usize)) % self.buckets.len()
}
fn insert(&mut self, key: String, value: i32) {
let index = self.hash(&key);
self.buckets[index].push((key, value));
}
}
This code creates a simple hash table that stores key-value pairs in buckets based on a hash function.
- Primary operation: Calculating the hash by adding byte values of the key.
- How many times: Once per insert or lookup, iterating over each byte of the key.
- Secondary operation: Searching inside the bucket list if needed (not shown here).
- How many times: Depends on bucket size, which grows with collisions.
As more items are added, the hash function still processes each key's bytes once.
However, if many keys hash to the same bucket, searching that bucket takes longer.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Hash calculation over key length + small bucket search |
| 100 | Hash calculation over key length + moderate bucket search if collisions occur |
| 1000 | Hash calculation over key length + longer bucket search if collisions increase |
Pattern observation: Hash calculation cost grows with key length, but bucket search cost grows with number of collisions.
Time Complexity: O(k + m)
This means time grows with key length k plus the number of items m in the bucket due to collisions.
[X] Wrong: "Hash table operations always take constant time no matter what."
[OK] Correct: If many keys collide, searching inside a bucket can take longer, so time depends on collisions and key length.
Understanding how hash functions and collisions affect speed helps you explain and improve data lookup in real systems.
What if we changed the hash function to a more complex one that takes longer to compute? How would the time complexity change?
Practice
Solution
Step 1: Understand the role of a hash function
A hash function takes a key and converts it into a number that can be used as an index in the hash table.Step 2: Identify the correct purpose
This index helps to quickly find where the value related to the key is stored.Final Answer:
To convert a key into an index for storing values -> Option BQuick Check:
Hash function = key to index [OK]
- Thinking hash functions sort keys
- Confusing hashing with encryption
- Believing hash functions count elements
Solution
Step 1: Recall Rust syntax for hash maps
In Rust, hash maps are created using HashMap::new() and usually declared mutable with let mut.Step 2: Check each option
let mut map = HashMap::new(); uses correct Rust syntax. Options A, B, and C use syntax from other languages or incorrect Rust syntax.Final Answer:
let mut map = HashMap::new(); -> Option AQuick Check:
Rust hash map creation = let mut map = HashMap::new() [OK]
- Using syntax from other languages like Java or JavaScript
- Forgetting to make the map mutable
- Missing the HashMap:: prefix
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 2);
map.insert("apple", 5);
println!("{:?}", map.get("apple"));
}Solution
Step 1: Understand insert behavior in HashMap
In Rust's HashMap, inserting a value with an existing key replaces the old value.Step 2: Trace the inserts and get
First insert: "apple" -> 3, second insert: "banana" -> 2, third insert: "apple" -> 5 replaces 3. So map.get("apple") returns Some(5).Final Answer:
Some(5) -> Option DQuick Check:
Last insert replaces value = Some(5) [OK]
- Assuming duplicate keys cause error
- Thinking old value remains after insert
- Expecting None for existing key
use std::collections::HashMap;
fn main() {
let map = HashMap::new();
map.insert("key", 10);
println!("{:?}", map.get("key"));
}Solution
Step 1: Check mutability of map
The map is declared with let (immutable) but insert tries to modify it.Step 2: Identify the error cause
In Rust, to modify a HashMap, it must be declared mutable with let mut.Final Answer:
map is immutable but insert is called -> Option CQuick Check:
Insert needs mutable map [OK]
- Forgetting to import HashMap
- Assuming get method is missing
- Thinking semicolon is missing
Solution
Step 1: Understand the required hash function
The function must sum ASCII values of all characters and then take modulo with size.Step 2: Analyze each option
fn hash(key: &str, size: usize) -> usize { key.bytes().map(|b| b as usize).sum::<usize>() % size } sums bytes (ASCII values) and mods by size correctly. Options A, B, and D only use length or count, not sum of ASCII values.Final Answer:
fn hash(key: &str, size: usize) -> usize { key.bytes().map(|b| b as usize).sum::<usize>() % size } -> Option AQuick Check:
Sum ASCII bytes then mod size = fn hash(key: &str, size: usize) -> usize { key.bytes().map(|b| b as usize).sum::<usize>() % size } [OK]
- Using length instead of sum of ASCII values
- Using chars count instead of bytes
- Forgetting to modulo by table size
Ready to prove it?
Pass the test and this pattern moves unknown
