← Home

208. Implement Trie (Prefix Tree)

LeetCode article · C++ solution
Website made by wuisabel-gif · Original C++ code by keineahnung2345
data structure designC++Markdown
208

I like to read this solution as a small machine: keep the useful information, throw away the noise. For 208. Implement Trie (Prefix Tree), the solution in this repository is mainly a data structure design solution.

Guide

What?

The first job is to translate the English prompt into state, transition, and stopping conditions. Instead of trying to be clever immediately, read the code as a sequence of questions:

  • What state are we keeping?
  • How do we move from one state to the next?
  • When do we know the answer is already determined?

For this file, the main tools are: data structure design, trie, prefix sums.

Guide

When?

This is the kind of solution you want when the problem has structure hiding inside a messy-looking input. The accepted code reduces that pressure by storing exactly the information that remains useful later.

The important function names to track are insert, search, startsWith, containsKey, put.

Guide

Why?

The code is doing bookkeeping so your brain does not have to keep the entire search space open at once.

  • A set is doing the membership or uniqueness work, which keeps the main loop readable.
  • The final return is not magic; it is the invariant after the loops or recursion have finished doing their accounting.

Guide

How?

Walk through the solution in this order:

  1. Initialize the memory or helper structure.
  2. Process candidates in the order the invariant expects.
  3. Update the answer only when the current state is valid.
  4. Return the value that represents the fully processed input.

The most important competitive-programming habit here is to trust the invariant. Once the invariant is right, the loops become much less scary.

Guide

Complexity

  • Time: O(n) to O(n log n), depending on the dominant loop or data structure operation
  • Space: O(n) in the usual case for auxiliary containers or recursion

Guide

C++ Solution

Your submission

The accepted solution

solution.cpp
01//Runtime: 548 ms, faster than 5.04% of C++ online submissions for Implement Trie (Prefix Tree).
02//Memory Usage: 23.9 MB, less than 100.00% of C++ online submissions for Implement Trie (Prefix Tree).
03
04class Trie {
05public:
06    set<string> words;
07    /** Initialize your data structure here. */
08    Trie() {
09    }
10    
11    /** Inserts a word into the trie. */
12    void insert(string word) {
13        words.insert(word);
14    }
15    
16    /** Returns if the word is in the trie. */
17    bool search(string word) {
18        return words.find(word)!=words.end();
19    }
20    
21    /** Returns if there is any word in the trie that starts with the given prefix. */
22    bool startsWith(string prefix) {
23        for(set<string>::iterator it = words.begin(); it!=words.end(); it++){
24            if((*it).rfind(prefix, 0) == 0) return true;
25        }
26        return false;
27    }
28};
29
30/**
31 * Your Trie object will be instantiated and called as such:
32 * Trie* obj = new Trie();
33 * obj->insert(word);
34 * bool param_2 = obj->search(word);
35 * bool param_3 = obj->startsWith(prefix);
36 */
37 
38//Runtime: 92 ms, faster than 59.89% of C++ online submissions for Implement Trie (Prefix Tree).
39//Memory Usage: 48 MB, less than 30.86% of C++ online submissions for Implement Trie (Prefix Tree).
40class TrieNode{
41private:
42    vector<TrieNode*> links;
43    int R = 26;
44    bool isEnd = false;
45    
46public:    
47    TrieNode() {
48        links = vector<TrieNode*>(R);
49    }
50    
51    bool containsKey(char c){
52        return links[c-'a'] != NULL;
53    }
54    
55    TrieNode* get(char c){
56        return links[c-'a'];
57    }
58    
59    void put(char c, TrieNode* node){
60        links[c-'a'] = node;
61    }
62    
63    void setEnd(){
64        isEnd = true;
65    }
66    
67    bool getEnd(){
68        return isEnd;
69    }
70};
71
72class Trie {
73private:
74    TrieNode *root;
75    
76    // search a prefix or whole key in trie and
77    // returns the node where search ends
78    TrieNode* searchPrefix(string word){
79        TrieNode *node = root;
80        for(char c : word){
81            if(node->containsKey(c)){
82                node = node->get(c);
83            }else{
84                return NULL;
85            }
86        }
87        return node;
88    }
89    
90public:
91    /** Initialize your data structure here. */
92    Trie() {
93        root = new TrieNode();
94    }
95    
96    /** Inserts a word into the trie. */
97    //time: O(m), space: O(m), m is the key length.
98    void insert(string word) {
99        TrieNode *node = root;
100        for(char c : word){
101            if(!node->containsKey(c)){
102                node->put(c, new TrieNode());
103            }
104            node = node->get(c);
105        }
106        node->setEnd();
107    }
108    
109    /** Returns if the word is in the trie. */
110    //time: O(m), space: O(1)
111    bool search(string word) {
112        TrieNode *node = searchPrefix(word);
113        return node != NULL && node->getEnd();
114    }
115    
116    /** Returns if there is any word in the trie that starts with the given prefix. */
117    //time: O(m), space: O(1)
118    bool startsWith(string prefix) {
119        TrieNode *node = searchPrefix(prefix);
120        return node != NULL;
121    }
122};
123
124//Runtime: 124 ms, faster than 45.63% of C++ online submissions for Implement Trie (Prefix Tree).
125//Memory Usage: 48.1 MB, less than 16.67% of C++ online submissions for Implement Trie (Prefix Tree).
126class TrieNode{
127public:
128    bool isEnd;
129    vector<TrieNode*> children;
130    char c;
131    
132    TrieNode(char c = '\0'){
133        isEnd = false;
134        this->c = c;
135        children = vector<TrieNode*>(26, nullptr);
136    }
137};
138
139class Trie {
140public:
141    TrieNode* root;
142    
143    /** Initialize your data structure here. */
144    Trie() {
145        root = new TrieNode();
146    }
147    
148    /** Inserts a word into the trie. */
149    void insert(string word) {
150        TrieNode* node = root;
151        for(char c : word){
152            if(node->children[c-'a'] == nullptr){
153                node->children[c-'a'] = new TrieNode(c);
154            }
155            node = node->children[c-'a'];
156        }
157        node->isEnd = true;
158    }
159    
160    /** Returns if the word is in the trie. */
161    bool search(string word) {
162        TrieNode* node = root;
163        for(char c : word){
164            if(node->children[c-'a'] == nullptr) return false;
165            node = node->children[c-'a'];
166        }
167        return node->isEnd;
168    }
169    
170    /** Returns if there is any word in the trie that starts with the given prefix. */
171    bool startsWith(string prefix) {
172        TrieNode* node = root;
173        for(char c : prefix){
174            if(node->children[c-'a'] == nullptr) return false;
175            node = node->children[c-'a'];
176        }
177        return true;
178    }
179};
180
181/**
182 * Your Trie object will be instantiated and called as such:
183 * Trie* obj = new Trie();
184 * obj->insert(word);
185 * bool param_2 = obj->search(word);
186 * bool param_3 = obj->startsWith(prefix);
187 */

Cost

Complexity

Time
O(n) to O(n log n), depending on the dominant loop or data structure operation
Dominated by the main traversal, recursion, or data-structure operations in the code.
Space
O(n) in the usual case for auxiliary containers or recursion
Auxiliary state plus the answer structure where the problem requires one.