Skip to content

Commit 32a7d0f

Browse files
committed
feat: add solutions to lc problem: No.1804
No.1804.Implement Trie II (Prefix Tree)
1 parent ee243fe commit 32a7d0f

File tree

6 files changed

+578
-156
lines changed

6 files changed

+578
-156
lines changed

solution/1800-1899/1804.Implement Trie II (Prefix Tree)/README.md

+204-52
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,24 @@ trie.countWordsStartingWith("app"); // 返回 0
5656

5757
<!-- 这里可写通用的实现逻辑 -->
5858

59+
**方法一:数组实现前缀树**
60+
5961
前缀树每个节点包括三部分:
6062

6163
1. 指向子节点的指针数组 children,对于本题而言,数组长度为 26,即小写英文字母的数量。`children[0]` 对应小写字母 a,...,`children[25]` 对应小写字母 z。
62-
1. int 变量 `count`,表示以该节点结尾的字符串个数。
63-
1. int 变量 `preCount`,表示以该节点作为前缀节点的字符串个数。
64+
1. int 变量 `v`,表示以该节点结尾的字符串个数。
65+
1. int 变量 `pv`,表示以该节点作为前缀节点的字符串个数。
6466

6567
### 1. 插入字符串
6668

6769
我们从字典树的根开始,插入字符串。对于当前字符对应的子节点,有两种情况:
6870

6971
- 子节点存在。沿着指针移动到子节点,继续处理下一个字符。
70-
- 子节点不存在。创建一个新的子节点,记录在 `children` 数组的对应位置上,然后沿着指针移动到子节点,让子节点的 `preCount` 值加 1。继续搜索下一个字符。
72+
- 子节点不存在。创建一个新的子节点,记录在 `children` 数组的对应位置上,然后沿着指针移动到子节点,让子节点的 `pv` 值加 1。继续搜索下一个字符。
73+
74+
重复以上步骤,直到处理字符串的最后一个字符,然后将当前节点的 `v` 值加 1。
7175

72-
重复以上步骤,直到处理字符串的最后一个字符,然后将当前节点的 `count` 值加 1
76+
时间复杂度 $O(n)$,其中 $n$ 为字符串的长度
7377

7478
### 2. 查找前缀
7579

@@ -80,6 +84,14 @@ trie.countWordsStartingWith("app"); // 返回 0
8084

8185
重复以上步骤,直到返回空指针或搜索完前缀的最后一个字符。
8286

87+
时间复杂度 $O(n)$,其中 $n$ 为字符串的长度。
88+
89+
### 3. 移除字符串
90+
91+
我们从字典树的根节点开始,依次将对应的子节点的 `pv` 值减 1,直到搜索完字符串的最后一个字符。然后将当前节点的 `v` 值减 1。
92+
93+
时间复杂度 $O(n)$,其中 $n$ 为字符串的长度。
94+
8395
<!-- tabs:start -->
8496

8597
### **Python3**
@@ -90,42 +102,41 @@ trie.countWordsStartingWith("app"); // 返回 0
90102
class Trie:
91103
def __init__(self):
92104
self.children = [None] * 26
93-
self.count = 0
94-
self.pre_count = 0
105+
self.v = self.pv = 0
95106

96107
def insert(self, word: str) -> None:
97108
node = self
98109
for c in word:
99-
index = ord(c) - ord('a')
100-
if node.children[index] is None:
101-
node.children[index] = Trie()
102-
node = node.children[index]
103-
node.pre_count += 1
104-
node.count += 1
110+
idx = ord(c) - ord('a')
111+
if node.children[idx] is None:
112+
node.children[idx] = Trie()
113+
node = node.children[idx]
114+
node.pv += 1
115+
node.v += 1
105116

106117
def countWordsEqualTo(self, word: str) -> int:
107-
node = self._search_prefix(word)
108-
return 0 if node is None else node.count
118+
node = self.search(word)
119+
return 0 if node is None else node.v
109120

110121
def countWordsStartingWith(self, prefix: str) -> int:
111-
node = self._search_prefix(prefix)
112-
return 0 if node is None else node.pre_count
122+
node = self.search(prefix)
123+
return 0 if node is None else node.pv
113124

114125
def erase(self, word: str) -> None:
115126
node = self
116127
for c in word:
117-
index = ord(c) - ord('a')
118-
node = node.children[index]
119-
node.pre_count -= 1
120-
node.count -= 1
128+
idx = ord(c) - ord('a')
129+
node = node.children[idx]
130+
node.pv -= 1
131+
node.v -= 1
121132

122-
def _search_prefix(self, prefix: str):
133+
def search(self, word):
123134
node = self
124-
for c in prefix:
125-
index = ord(c) - ord('a')
126-
if node.children[index] is None:
135+
for c in word:
136+
idx = ord(c) - ord('a')
137+
if node.children[idx] is None:
127138
return None
128-
node = node.children[index]
139+
node = node.children[idx]
129140
return node
130141

131142

@@ -143,57 +154,55 @@ class Trie:
143154

144155
```java
145156
class Trie {
146-
private Trie[] children;
147-
private int count;
148-
private int preCount;
157+
private Trie[] children = new Trie[26];
158+
private int v;
159+
private int pv;
149160

150161
public Trie() {
151-
children = new Trie[26];
152-
count = 0;
153-
preCount = 0;
162+
154163
}
155164

156165
public void insert(String word) {
157166
Trie node = this;
158-
for (int i = 0; i < word.length(); ++i) {
159-
int index = word.charAt(i) - 'a';
160-
if (node.children[index] == null) {
161-
node.children[index] = new Trie();
167+
for (char c : word.toCharArray()) {
168+
c -= 'a';
169+
if (node.children[c] == null) {
170+
node.children[c] = new Trie();
162171
}
163-
node = node.children[index];
164-
node.preCount += 1;
172+
node = node.children[c];
173+
++node.pv;
165174
}
166-
node.count += 1;
175+
++node.v;
167176
}
168177

169178
public int countWordsEqualTo(String word) {
170-
Trie node = searchPrefix(word);
171-
return node == null ? 0 : node.count;
179+
Trie node = search(word);
180+
return node == null ? 0 : node.v;
172181
}
173182

174183
public int countWordsStartingWith(String prefix) {
175-
Trie node = searchPrefix(prefix);
176-
return node == null ? 0 : node.preCount;
184+
Trie node = search(prefix);
185+
return node == null ? 0 : node.pv;
177186
}
178187

179188
public void erase(String word) {
180189
Trie node = this;
181-
for (int i = 0; i < word.length(); ++i) {
182-
int index = word.charAt(i) - 'a';
183-
node = node.children[index];
184-
node.preCount -= 1;
190+
for (char c : word.toCharArray()) {
191+
c -= 'a';
192+
node = node.children[c];
193+
--node.pv;
185194
}
186-
node.count -= 1;
195+
--node.v;
187196
}
188197

189-
private Trie searchPrefix(String prefix) {
198+
private Trie search(String word) {
190199
Trie node = this;
191-
for (int i = 0; i < prefix.length(); ++i) {
192-
int index = prefix.charAt(i) - 'a';
193-
if (node.children[index] == null) {
200+
for (char c : word.toCharArray()) {
201+
c -= 'a';
202+
if (node.children[c] == null) {
194203
return null;
195204
}
196-
node = node.children[index];
205+
node = node.children[c];
197206
}
198207
return node;
199208
}
@@ -209,6 +218,149 @@ class Trie {
209218
*/
210219
```
211220

221+
### **C++**
222+
223+
```cpp
224+
class Trie {
225+
public:
226+
Trie()
227+
: children(26)
228+
, v(0)
229+
, pv(0) {
230+
}
231+
232+
void insert(string word) {
233+
Trie* node = this;
234+
for (char c : word) {
235+
c -= 'a';
236+
if (!node->children[c]) {
237+
node->children[c] = new Trie();
238+
}
239+
node = node->children[c];
240+
++node->pv;
241+
}
242+
++node->v;
243+
}
244+
245+
int countWordsEqualTo(string word) {
246+
Trie* node = search(word);
247+
return node ? node->v : 0;
248+
}
249+
250+
int countWordsStartingWith(string prefix) {
251+
Trie* node = search(prefix);
252+
return node ? node->pv : 0;
253+
}
254+
255+
void erase(string word) {
256+
Trie* node = this;
257+
for (char c : word) {
258+
c -= 'a';
259+
node = node->children[c];
260+
--node->pv;
261+
}
262+
--node->v;
263+
}
264+
265+
private:
266+
vector<Trie*> children;
267+
int v, pv;
268+
269+
Trie* search(string& word) {
270+
Trie* node = this;
271+
for (char c : word) {
272+
c -= 'a';
273+
if (!node->children[c]) {
274+
return nullptr;
275+
}
276+
node = node->children[c];
277+
}
278+
return node;
279+
}
280+
};
281+
282+
/**
283+
* Your Trie object will be instantiated and called as such:
284+
* Trie* obj = new Trie();
285+
* obj->insert(word);
286+
* int param_2 = obj->countWordsEqualTo(word);
287+
* int param_3 = obj->countWordsStartingWith(prefix);
288+
* obj->erase(word);
289+
*/
290+
```
291+
292+
### **Go**
293+
294+
```go
295+
type Trie struct {
296+
children [26]*Trie
297+
v int
298+
pv int
299+
}
300+
301+
func Constructor() (_ Trie) { return }
302+
303+
func (this *Trie) Insert(word string) {
304+
node := this
305+
for _, c := range word {
306+
c -= 'a'
307+
if node.children[c] == nil {
308+
node.children[c] = &Trie{}
309+
}
310+
node = node.children[c]
311+
node.pv++
312+
}
313+
node.v++
314+
}
315+
316+
func (this *Trie) CountWordsEqualTo(word string) int {
317+
node := this.search(word)
318+
if node == nil {
319+
return 0
320+
}
321+
return node.v
322+
}
323+
324+
func (this *Trie) CountWordsStartingWith(prefix string) int {
325+
node := this.search(prefix)
326+
if node == nil {
327+
return 0
328+
}
329+
return node.pv
330+
}
331+
332+
func (this *Trie) Erase(word string) {
333+
node := this
334+
for _, c := range word {
335+
c -= 'a'
336+
node = node.children[c]
337+
node.pv--
338+
}
339+
node.v--
340+
}
341+
342+
func (this *Trie) search(word string) *Trie {
343+
node := this
344+
for _, c := range word {
345+
c -= 'a'
346+
if node.children[c] == nil {
347+
return nil
348+
}
349+
node = node.children[c]
350+
}
351+
return node
352+
}
353+
354+
/**
355+
* Your Trie object will be instantiated and called as such:
356+
* obj := Constructor();
357+
* obj.Insert(word);
358+
* param_2 := obj.CountWordsEqualTo(word);
359+
* param_3 := obj.CountWordsStartingWith(prefix);
360+
* obj.Erase(word);
361+
*/
362+
```
363+
212364
### **...**
213365

214366
```

0 commit comments

Comments
 (0)