0208. Implement Trie (Prefix Tree)¶
Trie (we pronounce "try") or prefix tree is a tree data structure used to retrieve a key in a strings dataset. There are various applications of this very efficient data structure, such as autocomplete and spellchecker.
Implement the Trie class:
Trie()
initializes the trie object.void insert(String word)
inserts the stringword
to the trie.boolean search(String word)
returnstrue
if the stringword
is in the trie (i.e., was inserted before), andfalse
otherwise.boolean startsWith(String prefix)
returnstrue
if there is a previously inserted stringword
that has the prefixprefix
, andfalse
otherwise.
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
Constraints:
1 <= word.length, prefix.length <= 2000
word
andprefix
consist of lowercase English letters.- At most
3 * 104
calls will be made toinsert
,search
, andstartsWith
.
Property for Trie Tree¶
- each node does not store complete word
- each path has a character, the word to current node is the path with all the characters
- each leaf node marks as
end
for telling it's a complete word - each leaf can store the frequency (7, 3, 4, 15, 12, 11, 5, 9) in this case
- in the same level, all the silbilings are not the same
- if just typing in the prefix, trie can print out all the suggesting silbilings
each node can has 26 (all lower case) children - pretty bad space complexity
cpp: dynamically generate 26 nodes all the time¶
struct TrieNode {
TrieNode* next[26];
bool is_word;
TrieNode(bool b = false)
: is_word(b)
{
memset(next, 0, sizeof(next)); // next store address instead of the value itself
}
};
class Trie {
private:
TrieNode* root;
TrieNode* find(string key)
{
TrieNode* p = root;
for (int i = 0; i < key.size() && p != NULL; ++i) {
p = p->next[key[i] - 'a'];
}
return p;
}
public:
/** Initialize your data structure here. */
Trie() { root = new TrieNode(); }
/** Inserts a word into the trie. */
void insert(string word)
{
TrieNode* p = root;
for (int i = 0; i < word.size(); ++i) {
if (p->next[word[i] - 'a'] == NULL) // not assign any value yet
p->next[word[i] - 'a'] = new TrieNode(); // only new when
p = p->next[word[i] - 'a'];
}
p->is_word = true;
}
/** Returns if the word is in the trie. */
bool search(string word)
{
TrieNode* p = find(word);
return p != NULL && p->is_word;
}
/** Returns if there is any word in the trie that starts with the given
* prefix. */
bool startsWith(string prefix) { return find(prefix) != NULL; }
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_3 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
Last update:
April 1, 2022