208. Implement Trie (Prefix Tree) Medium
A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.
Implement the Trie class:
Trie()Initializes the trie object.void insert(String word)Inserts the stringwordinto the trie.boolean search(String word)Returnstrueif the stringwordis in the trie (i.e., was inserted before), andfalseotherwise.boolean startsWith(String prefix)Returnstrueif there is a previously inserted stringwordthat has the prefixprefix, andfalseotherwise.
Example 1:
Input
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output
[null, null, true, false, true, null, true]
Explanation:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // return True
trie.search("app"); // return False
trie.startsWith("app"); // return True
trie.insert("app");
trie.search("app"); // return True
Approach
Input: Initializing empty object sequence executing string mutations checking states.
Output: Boolean validity responses upon tracking.
This is a classic Trie (Prefix Tree) problem.
(root)
/ / / | \ \ \
a s x f z p b
...A Trie is a tree structure used for efficiently storing and retrieving a collection of strings.
- Core Concept:
- Each node represents a character.
- Every path represents a prefix.
- At the last character of a word, use a flag (like
isWord = true) to distinguish between a "complete word" and "just a prefix".
Operational Logic
insert(word): Build nodes character by character, eventually markingisWord=true.search(word): Lookup character by character, finallyisWord=truemust be satisfied to return true.startsWith(prefix): Lookup character by character, return true as long as the path exists.
Complexity
- Insert / Search / Prefix Check:
O(m), wheremis the length of the string. - Trades space for time.
- Insert / Search / Prefix Check:
Implementation
class Trie:
def __init__(self):
# Initialize an empty dictionary to represent the root node of the Trie
self.trie = {}
def insert(self, word: str) -> None:
# Start inserting words from the root node of the Trie
node = self.trie
for char in word:
# If the current character is not in the current node, add it
if char not in node:
node[char] = {}
# Move to the next node
node = node[char]
# Mark the end of the word within the node, using '#'
node['#'] = True # '#' marks the end of a word
def search(self, word: str) -> bool:
# Start searching for the word from the root node
node = self.trie
for char in word:
# If the current character is not found, return False
if char not in node:
return False
# Move to the next node
node = node[char]
# Check if the current node is marked as the end of a word
return '#' in node
def startsWith(self, prefix: str) -> bool:
# Check prefixes starting from the root node
node = self.trie
for char in prefix:
# If the current character is not found, return False
if char not in node:
return False
# Move to the next node
node = node[char]
# If the loop completes successfully, the prefix exists
return True// Define Trie (Prefix Tree)
class Trie {
constructor() {
// Root node, use an object to store children map
this.root = {};
}
/**
* Insert a word into the Trie
* @param {string} word
* @return {void}
*/
insert(word) {
let curr = this.root;
for (let ch of word) {
// If the current node does not have this character, create a new child node
if (!curr[ch]) {
curr[ch] = {};
}
// Move to the child node
curr = curr[ch];
}
// Mark this path to be representing a fully formed word
curr.isWord = true;
}
/**
* Check if the completely formed word exists in the Trie
* @param {string} word
* @return {boolean}
*/
search(word) {
let curr = this.root;
for (let ch of word) {
// If character isn't found, return false
if (!curr[ch]) return false;
curr = curr[ch];
}
// Must be a complete word evaluating strictly to truthiness explicitly cast
return !!curr.isWord;
}
/**
* Determine if there is any path with matching prefix internally structure
* @param {string} prefix
* @return {boolean}
*/
startsWith(prefix) {
let curr = this.root;
for (let ch of prefix) {
if (!curr[ch]) return false;
curr = curr[ch];
}
return true;
}
}Complexity Analysis
- Time Complexity:
O(m)depends exclusively upon the search string length - Space Complexity:
O(m)bounding structure creation matching character types