Hash Map vs Array vs Linked List for Lookup in DSA Rust - Complexity Comparison
We want to understand how fast we can find an item in different data structures.
How does the time to look up a value change as the data grows?
Analyze the time complexity of the following lookup operations.
// Lookup in an array
fn lookup_array(arr: &[i32], target: i32) -> Option {
for (i, &val) in arr.iter().enumerate() {
if val == target {
return Some(i);
}
}
None
}
// Lookup in a linked list
struct Node {
val: i32,
next: Option>,
}
fn lookup_linked_list(head: &Option>, target: i32) -> bool {
let mut current = head;
while let Some(node) = current {
if node.val == target {
return true;
}
current = &node.next;
}
false
}
// Lookup in a hash map
use std::collections::HashMap;
fn lookup_hash_map(map: &HashMap, key: i32) -> Option<&String> {
map.get(&key)
}
This code shows how to find a value in an array, a linked list, and a hash map.
Look at what repeats when searching for the target.
- Array and Linked List: Loop through each element one by one.
- Hash Map: Use a direct calculation to find the value without looping.
- Dominant operation: The loop in array and linked list; constant time lookup in hash map.
As the number of items grows, how many steps do we take?
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Array/Linked List: up to 10 checks; Hash Map: about 1 check |
| 100 | Array/Linked List: up to 100 checks; Hash Map: about 1 check |
| 1000 | Array/Linked List: up to 1000 checks; Hash Map: about 1 check |
Pattern observation: Array and linked list grow linearly with input size; hash map stays almost constant.
Time Complexity: O(n) for array and linked list, O(1) average for hash map
This means array and linked list take longer as data grows, but hash map stays fast most of the time.
[X] Wrong: "Hash map lookup always takes the same time no matter what."
[OK] Correct: Sometimes hash maps have collisions that slow lookup, but on average they are very fast.
Knowing these differences helps you choose the right data structure and explain your choices clearly.
"What if the array was sorted? How would the time complexity of lookup change?"
Practice
Solution
Step 1: Understand lookup times for data structures
Hash Map uses hashing to find keys quickly, usually in constant time O(1).Step 2: Compare with Array and Linked List
Array and Linked List require searching elements linearly, which is slower (O(n)).Final Answer:
Hash Map -> Option DQuick Check:
Fastest lookup = Hash Map [OK]
- Thinking arrays have O(1) lookup for keys (only for indices)
- Confusing linked list traversal with direct access
- Assuming vector is same as hash map for lookup
map with String keys and i32 values?Solution
Step 1: Check correct syntax for mutable HashMap declaration
In Rust,let mutdeclares a mutable variable. The type isHashMap<String, i32>. The constructor isHashMap::new().Step 2: Identify errors in other options
let map = HashMap::new(); missesmut. let mut map = HashMap::new(); has wrong generic syntax. let map: HashMap = HashMap::new(); swaps key and value types.Final Answer:
let mut map: HashMap = HashMap::new(); -> Option BQuick Check:
Mutable HashMap syntax = let mut map: HashMap = HashMap::new(); [OK]
- Forgetting mut keyword for mutable map
- Wrong generic syntax with new()
- Swapping key and value types
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 2);
map.insert("orange", 5);
let lookup = map.get("banana");
println!("{:?}", lookup);
}Solution
Step 1: Understand HashMap get method return type
Thegetmethod returns anOptiontype:Some(&value)if key exists, elseNone.Step 2: Check if key "banana" exists and its value
Key "banana" exists with value 2, soget("banana")returnsSome(&2). Printing with {:?} showsSome(2).Final Answer:
Some(2) -> Option CQuick Check:
HashMap get returns Option = Some(2) [OK]
- Expecting direct value instead of Option
- Confusing None with Some
- Printing key instead of value
struct Node {
value: i32,
next: Option,
}
fn find(head: &Option, target: i32) -> bool {
let mut current = head;
while current.is_some() {
if current.as_ref().unwrap().value == target {
return true;
}
current = current.unwrap().next;
}
false
}Solution
Step 1: Analyze ownership in while loop
Callingunwrap()oncurrentmoves ownership, socurrentcannot be used again.Step 2: Identify why unwrap() causes error
Usingunwrap()twice consumescurrent, causing ownership error. Instead, use references or pattern matching.Final Answer:
Using unwrap() twice causes ownership error -> Option AQuick Check:
Ownership error due to unwrap() usage [OK]
- Ignoring ownership rules with unwrap()
- Thinking mutable needed for current here
- Confusing Box with reference types
Solution
Step 1: Consider lookup speed for large collections
Linked List has O(n) lookup, which is slow. Array lookup by index is O(1), but only if keys are indices, not arbitrary integers.Step 2: Evaluate Hash Map and Vector
Hash Map provides average O(1) lookup for arbitrary keys like user IDs. Vector lookup by index is O(1), but user IDs may not be continuous indices.Final Answer:
Hash Map, because it offers average constant time lookup for keys -> Option AQuick Check:
Best lookup for arbitrary keys = Hash Map [OK]
- Assuming array lookup works for arbitrary keys
- Choosing linked list for speed
- Confusing vector with hash map for key lookup
Ready to prove it?
Pass the test and this pattern moves unknown
