Why Trie Exists and What Hash Map Cannot Do for Strings in DSA C - Performance Analysis
We want to understand why tries are used for strings instead of hash maps.
What makes tries faster or better for some string tasks?
Analyze the time complexity of inserting and searching strings in a trie.
// Insert a word into a trie
void insert(struct TrieNode* root, const char* word) {
struct TrieNode* node = root;
for (int i = 0; word[i] != '\0'; i++) {
int index = word[i] - 'a';
if (!node->children[index]) {
node->children[index] = createNode();
}
node = node->children[index];
}
node->isEndOfWord = 1;
}
This code inserts each character of a word into a trie node by node.
Look at the loop that goes through each character of the word.
- Primary operation: Loop over each character in the input string.
- How many times: Exactly once per character, so length of the word.
As the word gets longer, the number of steps grows directly with its length.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 steps (one per character) |
| 100 | 100 steps |
| 1000 | 1000 steps |
Pattern observation: The work grows linearly with the length of the string.
Time Complexity: O(n)
This means the time to insert or search depends only on the string length, not the number of stored words.
[X] Wrong: "Hash maps always give O(1) time for string search, so tries are not needed."
[OK] Correct: Hash maps depend on hashing the whole string, which can take O(n) time per operation and can cause collisions. Tries avoid collisions and give predictable O(n) time based on string length, independent of number of stored words.
Understanding why tries exist helps you explain trade-offs in data structures for strings clearly and confidently.
What if we used a hash map that stores prefixes of strings instead of whole strings? How would that affect time complexity?
Practice
Solution
Step 1: Understand Trie and Hash Map capabilities
Trie stores strings in a tree form allowing prefix-based queries efficiently. Hash Map stores key-value pairs but does not support prefix search efficiently.Step 2: Compare prefix search ability
Trie can quickly find all strings starting with a prefix. Hash Map requires checking all keys or extra structures, which is inefficient.Final Answer:
Because Trie supports efficient prefix search which Hash Map cannot do easily -> Option BQuick Check:
Prefix search = Trie [OK]
- Thinking Hash Map cannot store strings as keys
- Believing Trie always uses less memory
- Assuming Trie is faster for all queries
Solution
Step 1: Understand Trie node structure
A Trie node typically has an array of pointers to child nodes (for each alphabet letter) and a boolean to mark end of word.Step 2: Check each option
struct TrieNode { struct TrieNode* children[26]; bool isEndOfWord; }; correctly declares an array of 26 pointers and a boolean flag. Others have wrong types or missing pointers.Final Answer:
struct TrieNode { struct TrieNode* children[26]; bool isEndOfWord; }; -> Option CQuick Check:
Trie node = array of pointers + bool [OK]
- Using int instead of pointer array for children
- Declaring children as single struct instead of array
- Confusing data types for isEndOfWord
insert("cat");
insert("car");
bool result = startsWith("ca");
printf("%d", result);Solution
Step 1: Understand insert and startsWith functions
Inserting "cat" and "car" adds nodes for 'c', 'a', 't' and 'r'. startsWith("ca") checks if any word starts with "ca".Step 2: Check if prefix "ca" exists
Both words start with "ca", so startsWith("ca") returns true (1).Final Answer:
1 -> Option AQuick Check:
Prefix "ca" found = 1 [OK]
- Assuming startsWith returns 0 if prefix is partial
- Confusing insert with search function
- Ignoring that both words share prefix
void insert(struct TrieNode* root, char* word) {
struct TrieNode* node = root;
for (int i = 0; word[i] != '\0'; i++) {
int index = word[i] - 'a';
if (node->children[index] == NULL) {
node->children[index] = malloc(sizeof(struct TrieNode));
}
node = node->children[index];
}
node->isEndOfWord = true;
}Solution
Step 1: Analyze node allocation
When malloc allocates new TrieNode, its children pointers and isEndOfWord are uninitialized garbage values.Step 2: Identify missing initialization
New nodes must have children array set to NULL and isEndOfWord set to false to avoid errors during traversal.Final Answer:
New nodes are not initialized to NULL -> Option DQuick Check:
Uninitialized nodes cause bugs [OK]
- Thinking index calculation is wrong
- Setting isEndOfWord too early
- Assuming root node passing is incorrect
Solution
Step 1: Understand prefix search complexity
Trie can find all words with a prefix in time proportional to prefix length (O(k)), independent of dictionary size.Step 2: Identify Hash Map limitation
Hash Map supports exact key lookup efficiently but cannot efficiently find keys by prefix without scanning all keys.Final Answer:
Trie allows prefix search in O(k) time; Hash Map cannot do prefix search efficiently -> Option AQuick Check:
Prefix search = Trie advantage [OK]
- Believing Hash Map cannot store strings as keys
- Thinking Trie uses less memory always
- Confusing sorting with prefix search
Ready to prove it?
Pass the test and this pattern moves unknown
