211. Design Add and Search Words Data Structure
Problem Description
This problem asks you to design a data structure called WordDictionary
that can store words and search for them with a special wildcard feature.
The data structure needs to support two operations:
-
addWord(word)
: Adds a word to the dictionary. The word consists only of lowercase English letters. -
search(word)
: Searches for a word in the dictionary. The search query can contain:- Regular lowercase English letters that must match exactly
- The wildcard character
'.'
which can match any single letter
For example, if you add the words "bad", "dad", and "mad" to the dictionary:
- Searching for "bad" returns
True
(exact match exists) - Searching for "pad" returns
False
(no exact match) - Searching for ".ad" returns
True
(matches "bad", "dad", and "mad" where '.' can be 'b', 'd', or 'm') - Searching for "b.." returns
True
(matches "bad" where the two dots match 'a' and 'd')
The solution uses a Trie (prefix tree) data structure to efficiently store and search words. Each node in the Trie has 26 children (one for each lowercase letter) and a flag indicating if it marks the end of a valid word.
The addWord
operation traverses the Trie, creating nodes as needed for each character in the word, and marks the final node as a word ending.
The search
operation uses recursion to handle the wildcard character '.'
. When encountering a dot, it explores all possible branches in the Trie at that position. For regular characters, it follows the specific branch if it exists. The search succeeds if it reaches a node marked as a word ending after processing all characters.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The Trie data structure is essentially a tree, which is a special type of graph. Each node represents a prefix, and edges represent character transitions.
Is it a tree?
- Yes: A Trie is specifically a tree structure where each node can have up to 26 children (one for each lowercase letter), and there's a single root node.
DFS
- Yes: The search operation, especially when handling the wildcard character
'.'
, requires exploring multiple branches of the tree. When we encounter a'.'
, we need to recursively explore all possible child nodes, which is a classic depth-first search pattern.
Conclusion: The flowchart leads us to use DFS (Depth-First Search) for this problem. This makes perfect sense because:
- The Trie is a tree structure that we need to traverse
- The wildcard search requires exploring multiple paths when encountering
'.'
- DFS with backtracking allows us to explore each possible branch recursively and return true if any path matches the search pattern
- The recursive nature of DFS perfectly handles the variable depth traversal needed for words of different lengths
Intuition
When we need to store and search for words efficiently, especially with pattern matching, we should think about how words share common prefixes. For instance, "bad", "bat", and "ban" all start with "ba". Instead of storing each word separately, we can build a tree-like structure where common prefixes are shared.
This naturally leads us to a Trie (prefix tree), where each path from root to a leaf represents a word. Each node stores whether it marks the end of a valid word, and has up to 26 children (one for each letter).
The real challenge comes with the wildcard search using '.'
. When we encounter a regular letter, we simply follow the corresponding branch if it exists. But when we hit a '.'
, we need to explore all possible branches at that level since '.'
can match any letter.
This "explore all possibilities" requirement screams for a recursive DFS approach. At each '.'
, we branch out to all existing children and continue searching from each one. If any branch leads to a successful match, we return True
. This is essentially a backtracking pattern - we try one path, and if it doesn't work, we backtrack and try another.
The beauty of this approach is that the recursive DFS naturally handles the variable depth of the search (words of different lengths) and the branching factor when encountering wildcards. The recursion stack keeps track of our current position in both the search word and the Trie, making the implementation clean and intuitive.
For example, when searching for "b.." in a Trie containing "bad", we start at root, move to 'b', then at the first '.'
we find child 'a' exists, move there, then at the second '.'
we find child 'd' exists and it's marked as a word ending - success!
Solution Approach
The implementation consists of two classes: Trie
and WordDictionary
.
Trie Node Structure:
Each Trie
node contains:
children
: An array of 26 elements (for 'a' to 'z'), wherechildren[i]
points to the child node for characterchr(ord('a') + i)
is_end
: A boolean flag indicating if this node marks the end of a valid word
Adding Words (addWord
):
- Start from the root of the Trie
- For each character
c
in the word:- Calculate its index:
idx = ord(c) - ord('a')
- If
node.children[idx]
doesn't exist, create a new Trie node there - Move to the child node:
node = node.children[idx]
- Calculate its index:
- After processing all characters, mark the final node with
node.is_end = True
Searching with Wildcards (search
):
The search uses a recursive helper function that takes the remaining word to search and the current node:
-
Iterate through each character in the word:
- If it's a regular letter and the corresponding child doesn't exist, return
False
- If it's a
'.'
wildcard:- Try all non-null children recursively
- For each child, search the remaining substring
word[i+1:]
starting from that child - If any recursive call returns
True
, we found a match - If no child leads to a match, return
False
- If it's a regular letter with an existing child, move to that child
- If it's a regular letter and the corresponding child doesn't exist, return
-
After processing all characters, return
node.is_end
to check if we've reached a valid word ending
Key Implementation Details:
- The wildcard handling uses DFS to explore all possible branches when encountering
'.'
- The recursion naturally handles backtracking - if one branch fails, we try the next
- String slicing
word[i+1:]
passes the remaining portion of the word to recursive calls - The base case occurs when we've processed all characters and check
is_end
Time Complexity:
addWord
: O(m) where m is the length of the wordsearch
: O(n ร 26^k) worst case, where n is the word length and k is the number of dots (since each dot requires exploring up to 26 branches)
Space Complexity:
- O(ALPHABET_SIZE ร N ร M) for storing the Trie, where N is the number of words and M is the average length
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to see how the Trie-based solution works.
Step 1: Initialize and Add Words
Start with an empty WordDictionary and add three words: "bad", "dad", "mad"
Initial: root = Trie() Adding "bad": - Start at root - 'b': Create child at index 1 (b-a=1), move there - 'a': Create child at index 0 (a-a=0), move there - 'd': Create child at index 3 (d-a=3), move there - Mark is_end = True Adding "dad": - Start at root - 'd': Create child at index 3 (d-a=3), move there - 'a': Create child at index 0, move there - 'd': Create child at index 3, move there - Mark is_end = True Adding "mad": - Start at root - 'm': Create child at index 12 (m-a=12), move there - 'a': Create child at index 0, move there - 'd': Create child at index 3, move there - Mark is_end = True
The resulting Trie structure looks like:
root / | \ b d m | | | a a a | | | d* d* d* (* indicates is_end = True)
Step 2: Search for ".ad"
Now let's search for the pattern ".ad" which should return True:
-
Start at root with word = ".ad", index = 0
-
First character is '.', so we need to try all non-null children of root
-
Root has three children at indices 1('b'), 3('d'), and 12('m')
Branch 1: Try child 'b'
- Recursively search for "ad" starting from the 'b' node
- Next char 'a': Check children[0] of 'b' node โ exists, move there
- Next char 'd': Check children[3] of 'a' node โ exists, move there
- Reached end of pattern, check is_end โ True
- Return True (found "bad")
Since we found a match, the search returns True immediately without needing to check the other branches.
Step 3: Search for "b.."
Let's trace through searching for "b..":
- Start at root with word = "b..", index = 0
- First character is 'b': Check children[1] โ exists, move to 'b' node
- Second character is '.': Try all non-null children of 'b' node
- 'b' node only has one child at index 0 ('a')
- Recursively search for "." starting from 'a' node
- Next char is '.': Try all non-null children of 'a' node
- 'a' node only has one child at index 3 ('d')
- Recursively search for "" (empty) starting from 'd' node
- No more characters to process
- Check is_end of 'd' node โ True
- Return True (found "bad")
Step 4: Search for "b.d" (False case)
- Start at root with word = "b.d", index = 0
- First character is 'b': Check children[1] โ exists, move to 'b' node
- Second character is '.': Try all non-null children of 'b' node
- Only child is at index 0 ('a'), so recursively search for "d" from 'a' node
- From 'a' node, look for 'd': Check children[3] โ exists, move to 'd' node
- Reached end of pattern, check is_end โ True
- Return True (found "bad")
Wait, this should return True! Let me trace a false case instead:
Step 4 (corrected): Search for "ba" (False case)
- Start at root with word = "ba", index = 0
- First character is 'b': Check children[1] โ exists, move to 'b' node
- Second character is 'a': Check children[0] โ exists, move to 'a' node
- Reached end of pattern, check is_end of 'a' node โ False (not a word ending)
- Return False
This returns False because although the path "ba" exists in our Trie, it's not marked as a complete word - only "bad" is marked as complete.
Solution Implementation
1class TrieNode:
2 """Represents a node in the Trie data structure."""
3
4 def __init__(self):
5 # Array to store references to child nodes (26 letters a-z)
6 self.children = [None] * 26
7 # Flag to mark if this node represents the end of a word
8 self.is_end_of_word = False
9
10
11class WordDictionary:
12 """
13 Data structure that supports adding words and searching with wildcard '.' support.
14 Uses a Trie (prefix tree) for efficient storage and retrieval.
15 """
16
17 def __init__(self):
18 """Initialize the WordDictionary with an empty Trie."""
19 self.root = TrieNode()
20
21 def addWord(self, word: str) -> None:
22 """
23 Add a word to the dictionary.
24
25 Args:
26 word: The word to be added (contains only lowercase letters)
27 """
28 current_node = self.root
29
30 # Traverse through each character in the word
31 for char in word:
32 # Calculate the index for this character (0-25 for a-z)
33 index = ord(char) - ord('a')
34
35 # Create a new node if path doesn't exist
36 if current_node.children[index] is None:
37 current_node.children[index] = TrieNode()
38
39 # Move to the child node
40 current_node = current_node.children[index]
41
42 # Mark the end of the word
43 current_node.is_end_of_word = True
44
45 def search(self, word: str) -> bool:
46 """
47 Search for a word in the dictionary.
48 Supports '.' as a wildcard that can match any single character.
49
50 Args:
51 word: The word pattern to search for
52
53 Returns:
54 True if the word exists in the dictionary, False otherwise
55 """
56
57 def dfs_search(word_substring: str, node: TrieNode) -> bool:
58 """
59 Helper function to recursively search with wildcard support.
60
61 Args:
62 word_substring: Remaining part of the word to search
63 node: Current node in the Trie
64
65 Returns:
66 True if pattern matches, False otherwise
67 """
68 # Process each character in the remaining word
69 for i in range(len(word_substring)):
70 char = word_substring[i]
71
72 if char == '.':
73 # Wildcard: try all possible children
74 for child_node in node.children:
75 if child_node is not None:
76 # Recursively search with remaining substring
77 if dfs_search(word_substring[i + 1:], child_node):
78 return True
79 # No valid path found through any child
80 return False
81 else:
82 # Regular character: check if path exists
83 index = ord(char) - ord('a')
84 if node.children[index] is None:
85 return False
86 # Move to the next node
87 node = node.children[index]
88
89 # Check if we've reached a valid word ending
90 return node.is_end_of_word
91
92 # Start search from the root
93 return dfs_search(word, self.root)
94
95
96# Your WordDictionary object will be instantiated and called as such:
97# obj = WordDictionary()
98# obj.addWord(word)
99# param_2 = obj.search(word)
100
1/**
2 * Trie node structure for storing words
3 * Each node contains an array of 26 children (for lowercase letters a-z)
4 * and a boolean flag to mark the end of a word
5 */
6class Trie {
7 Trie[] children = new Trie[26];
8 boolean isEnd;
9}
10
11/**
12 * Data structure that supports adding words and searching with wildcard '.'
13 * Uses a Trie (prefix tree) for efficient storage and retrieval
14 */
15class WordDictionary {
16 private Trie root;
17
18 /**
19 * Initialize the WordDictionary with an empty Trie
20 */
21 public WordDictionary() {
22 root = new Trie();
23 }
24
25 /**
26 * Adds a word to the data structure
27 * @param word The word to be added (contains only lowercase letters)
28 */
29 public void addWord(String word) {
30 Trie currentNode = root;
31
32 // Traverse through each character in the word
33 for (char ch : word.toCharArray()) {
34 int index = ch - 'a';
35
36 // Create a new node if path doesn't exist
37 if (currentNode.children[index] == null) {
38 currentNode.children[index] = new Trie();
39 }
40
41 // Move to the next node
42 currentNode = currentNode.children[index];
43 }
44
45 // Mark the end of the word
46 currentNode.isEnd = true;
47 }
48
49 /**
50 * Search for a word in the data structure
51 * @param word The word to search (may contain '.' as wildcard for any letter)
52 * @return true if the word exists, false otherwise
53 */
54 public boolean search(String word) {
55 return searchHelper(word, root);
56 }
57
58 /**
59 * Helper method for recursive search with wildcard support
60 * @param word The word or remaining substring to search
61 * @param currentNode The current Trie node being examined
62 * @return true if the word pattern matches, false otherwise
63 */
64 private boolean searchHelper(String word, Trie currentNode) {
65 // Process each character in the word
66 for (int i = 0; i < word.length(); i++) {
67 char ch = word.charAt(i);
68
69 if (ch == '.') {
70 // Wildcard case: try all possible children
71 for (Trie childNode : currentNode.children) {
72 if (childNode != null &&
73 searchHelper(word.substring(i + 1), childNode)) {
74 return true;
75 }
76 }
77 return false;
78 } else {
79 // Regular character case
80 int index = ch - 'a';
81
82 // If no child exists for this character, word doesn't exist
83 if (currentNode.children[index] == null) {
84 return false;
85 }
86
87 // Move to the next node
88 currentNode = currentNode.children[index];
89 }
90 }
91
92 // Check if we've reached a valid word ending
93 return currentNode.isEnd;
94 }
95}
96
97/**
98 * Your WordDictionary object will be instantiated and called as such:
99 * WordDictionary obj = new WordDictionary();
100 * obj.addWord(word);
101 * boolean param_2 = obj.search(word);
102 */
103
1/**
2 * Trie (Prefix Tree) Node Implementation
3 * Used to store words character by character in a tree structure
4 */
5class TrieNode {
6public:
7 // Array to store pointers to child nodes (26 for lowercase English letters)
8 vector<TrieNode*> children;
9 // Flag to mark if this node represents the end of a word
10 bool isEndOfWord;
11
12 // Constructor initializes children array and end flag
13 TrieNode() {
14 children = vector<TrieNode*>(26, nullptr);
15 isEndOfWord = false;
16 }
17
18 // Insert a word into the trie starting from this node
19 void insert(const string& word) {
20 TrieNode* currentNode = this;
21
22 // Traverse each character in the word
23 for (char ch : word) {
24 // Convert character to index (0-25)
25 int index = ch - 'a';
26
27 // Create new node if path doesn't exist
28 if (currentNode->children[index] == nullptr) {
29 currentNode->children[index] = new TrieNode();
30 }
31
32 // Move to the child node
33 currentNode = currentNode->children[index];
34 }
35
36 // Mark the last node as end of word
37 currentNode->isEndOfWord = true;
38 }
39};
40
41/**
42 * Design a data structure that supports adding new words and finding if a string matches
43 * any previously added string. Supports wildcard '.' that can match any single character.
44 */
45class WordDictionary {
46private:
47 TrieNode* root; // Root node of the trie
48
49 /**
50 * Depth-first search helper function to handle wildcard matching
51 * @param word - The word pattern to search
52 * @param index - Current position in the word being processed
53 * @param currentNode - Current trie node being examined
54 * @return true if pattern matches, false otherwise
55 */
56 bool dfsSearch(const string& word, int index, TrieNode* currentNode) {
57 // Base case: reached end of word
58 if (index == word.size()) {
59 return currentNode->isEndOfWord;
60 }
61
62 char currentChar = word[index];
63
64 // Case 1: Regular character (not wildcard)
65 if (currentChar != '.') {
66 int childIndex = currentChar - 'a';
67 TrieNode* childNode = currentNode->children[childIndex];
68
69 // Continue search if child exists
70 if (childNode != nullptr && dfsSearch(word, index + 1, childNode)) {
71 return true;
72 }
73 }
74 // Case 2: Wildcard character '.'
75 else {
76 // Try all possible children
77 for (TrieNode* childNode : currentNode->children) {
78 if (childNode != nullptr && dfsSearch(word, index + 1, childNode)) {
79 return true;
80 }
81 }
82 }
83
84 return false;
85 }
86
87public:
88 // Constructor initializes the root node
89 WordDictionary() : root(new TrieNode()) {}
90
91 /**
92 * Add a word to the data structure
93 * @param word - The word to be added (contains only lowercase letters)
94 */
95 void addWord(string word) {
96 root->insert(word);
97 }
98
99 /**
100 * Search if there is any word in the data structure that matches the given pattern
101 * @param word - The pattern to search (may contain '.' as wildcard)
102 * @return true if pattern matches any word, false otherwise
103 */
104 bool search(string word) {
105 return dfsSearch(word, 0, root);
106 }
107};
108
109/**
110 * Your WordDictionary object will be instantiated and called as such:
111 * WordDictionary* obj = new WordDictionary();
112 * obj->addWord(word);
113 * bool param_2 = obj->search(word);
114 */
115
1/**
2 * Trie (Prefix Tree) Node Implementation
3 * Used to store words character by character in a tree structure
4 */
5class TrieNode {
6 // Array to store pointers to child nodes (26 for lowercase English letters)
7 children: (TrieNode | null)[];
8 // Flag to mark if this node represents the end of a word
9 isEndOfWord: boolean;
10
11 // Constructor initializes children array and end flag
12 constructor() {
13 this.children = new Array(26).fill(null);
14 this.isEndOfWord = false;
15 }
16
17 /**
18 * Insert a word into the trie starting from this node
19 * @param word - The word to be inserted
20 */
21 insert(word: string): void {
22 let currentNode: TrieNode = this;
23
24 // Traverse each character in the word
25 for (const char of word) {
26 // Convert character to index (0-25)
27 const index = char.charCodeAt(0) - 'a'.charCodeAt(0);
28
29 // Create new node if path doesn't exist
30 if (currentNode.children[index] === null) {
31 currentNode.children[index] = new TrieNode();
32 }
33
34 // Move to the child node
35 currentNode = currentNode.children[index]!;
36 }
37
38 // Mark the last node as end of word
39 currentNode.isEndOfWord = true;
40 }
41}
42
43// Root node of the trie
44let root: TrieNode;
45
46/**
47 * Depth-first search helper function to handle wildcard matching
48 * @param word - The word pattern to search
49 * @param index - Current position in the word being processed
50 * @param currentNode - Current trie node being examined
51 * @returns true if pattern matches, false otherwise
52 */
53function dfsSearch(word: string, index: number, currentNode: TrieNode): boolean {
54 // Base case: reached end of word
55 if (index === word.length) {
56 return currentNode.isEndOfWord;
57 }
58
59 const currentChar = word[index];
60
61 // Case 1: Regular character (not wildcard)
62 if (currentChar !== '.') {
63 const childIndex = currentChar.charCodeAt(0) - 'a'.charCodeAt(0);
64 const childNode = currentNode.children[childIndex];
65
66 // Continue search if child exists
67 if (childNode !== null && dfsSearch(word, index + 1, childNode)) {
68 return true;
69 }
70 }
71 // Case 2: Wildcard character '.'
72 else {
73 // Try all possible children
74 for (const childNode of currentNode.children) {
75 if (childNode !== null && dfsSearch(word, index + 1, childNode)) {
76 return true;
77 }
78 }
79 }
80
81 return false;
82}
83
84/**
85 * Initialize the WordDictionary data structure
86 */
87function WordDictionary(): void {
88 root = new TrieNode();
89}
90
91/**
92 * Add a word to the data structure
93 * @param word - The word to be added (contains only lowercase letters)
94 */
95function addWord(word: string): void {
96 root.insert(word);
97}
98
99/**
100 * Search if there is any word in the data structure that matches the given pattern
101 * @param word - The pattern to search (may contain '.' as wildcard)
102 * @returns true if pattern matches any word, false otherwise
103 */
104function search(word: string): boolean {
105 return dfsSearch(word, 0, root);
106}
107
108/**
109 * Your WordDictionary object will be instantiated and called as such:
110 * WordDictionary();
111 * addWord(word);
112 * let param_2 = search(word);
113 */
114
Time and Space Complexity
Time Complexity:
-
addWord(word)
:O(m)
wherem
is the length of the word being added. We traverse through each character of the word exactly once, and for each character, we perform constant time operations (array indexing and node creation). -
search(word)
:- Best case:
O(m)
wherem
is the length of the search word, when there are no wildcard characters ('.'). We simply traverse down the trie following the exact path. - Worst case:
O(n ร 26^m)
wherem
is the length of the search word andn
is the total number of nodes in the trie. This occurs when the word consists entirely of wildcards ('.'). At each '.', we potentially explore all 26 branches, and for each branch, we recursively search the remaining substring. In the absolute worst case with all dots, we could explore26^m
different paths. - Average case with wildcards:
O(n ร 26^k)
wherek
is the number of wildcard characters in the search word.
- Best case:
Space Complexity:
-
addWord(word)
:O(m)
in the worst case wherem
is the length of the word. If none of the characters in the word share a common prefix with existing words, we createm
new Trie nodes. -
search(word)
:O(m)
for the recursion stack depth, wherem
is the length of the search word. In the worst case with wildcards, the recursive calls can go as deep as the length of the word. -
Overall space complexity of the data structure:
O(ALPHABET_SIZE ร N ร M)
=O(26 ร N ร M)
whereN
is the total number of words inserted andM
is the average length of the words. Each Trie node contains an array of 26 pointers to children nodes. In the worst case where no words share common prefixes, we would haveN ร M
nodes, each with a 26-element array.
Common Pitfalls
1. Inefficient String Slicing in Recursion
The most common performance pitfall in this solution is the use of string slicing word_substring[i + 1:]
during recursive calls. This creates a new string object every time, leading to O(n) extra time and space complexity for each recursive call.
Problem Example:
# Current inefficient approach if dfs_search(word_substring[i + 1:], child_node): # Creates new string return True
Solution: Use an index pointer instead of string slicing:
def search(self, word: str) -> bool:
def dfs_search(index: int, node: TrieNode) -> bool:
# Base case: reached end of word
if index == len(word):
return node.is_end_of_word
char = word[index]
if char == '.':
# Wildcard: try all possible children
for child_node in node.children:
if child_node is not None:
# Pass index+1 instead of slicing
if dfs_search(index + 1, child_node):
return True
return False
else:
# Regular character
char_index = ord(char) - ord('a')
if node.children[char_index] is None:
return False
return dfs_search(index + 1, node.children[char_index])
return dfs_search(0, self.root)
2. Forgetting to Handle Empty String Edge Case
The code doesn't explicitly handle the case where an empty string is added or searched. While the current implementation technically works (empty string returns True only if root.is_end_of_word is True), it's better to be explicit.
Solution: Add explicit checks:
def addWord(self, word: str) -> None:
if not word: # Handle empty string
self.root.is_end_of_word = True
return
# ... rest of the implementation
def search(self, word: str) -> bool:
if not word: # Handle empty string
return self.root.is_end_of_word
# ... rest of the implementation
3. Not Optimizing the Wildcard Search
When encountering a wildcard, the current approach iterates through all 26 children even if many are None. This can be optimized by maintaining a list of actual children or checking early termination conditions.
Solution: Early termination and optimization:
def dfs_search(index: int, node: TrieNode) -> bool:
if index == len(word):
return node.is_end_of_word
char = word[index]
if char == '.':
# Early termination: if this is the last character
if index == len(word) - 1:
# Just check if any child has is_end_of_word = True
for child in node.children:
if child and child.is_end_of_word:
return True
return False
# Continue searching for non-last characters
for child in node.children:
if child and dfs_search(index + 1, child):
return True
return False
else:
char_index = ord(char) - ord('a')
if node.children[char_index] is None:
return False
return dfs_search(index + 1, node.children[char_index])
4. Memory Waste with Fixed-Size Arrays
Using [None] * 26
for every node wastes memory when words share few common prefixes or when the dictionary is sparse.
Solution: Use a dictionary instead of a fixed array:
class TrieNode:
def __init__(self):
self.children = {} # Use dictionary instead of array
self.is_end_of_word = False
def addWord(self, word: str) -> None:
current_node = self.root
for char in word:
if char not in current_node.children:
current_node.children[char] = TrieNode()
current_node = current_node.children[char]
current_node.is_end_of_word = True
These optimizations significantly improve both time and space efficiency, especially for large datasets with many wildcard searches.
Which data structure is used in a depth first search?
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
Trie Introduction Imagine you have a magic dictionary where as soon as you start spelling a word it immediately tells you how many words that start with those letters How cool would that be This is exactly the kind of magic a data structure called trie performs What's a Trie
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donโt Miss This!