LeetCode_208_实现Trie(前缀树)
廖家龙 用心听,不照做
1
2
3
4
5
6
7
8
9
10
11
字典树:

字典树(Trie)用于判断字符串是否存在或者是否具有某种字符串前缀。

为什么需要用字典树解决这类问题呢?
假设我们有一个储存了近万个单词的字典,即使我们使用哈希,在其中搜索一个单词的实际开销也是非常大的,且无法轻易支持搜索单词前缀。然而由于一个英文单词的长度n通常在10以内,如果我们使用字典树,则可以在O(n)--近似O(1)的时间内完成搜索,且额外开销非常小。

字典树的性质:
1. Trie的形状和单词的插入或删除顺序无关,也就是说对于任意给定的一组单词,Trie的形状都是唯一的。
2. 查找或插入一个长度为 L 的单词,访问 next 数组的次数最多为 L+1,和 Trie 中包含多少个单词无关。
3. Trie 的每个结点中都保留着一个字母表,这是很耗费空间的。如果 Trie 的高度为 n,字母表的大小为 m,最坏的情况是 Trie 中还不存在前缀相同的单词,那空间复杂度就为 O(m^n)

题目描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:
1. Trie() 初始化前缀树对象。
2. void insert(String word) 向前缀树中插入字符串 word 。
3. boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
4. boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。

示例:

输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True

提示:
1. 1 <= word.length, prefix.length <= 2000
2. word 和 prefix 仅由小写英文字母组成
3. insert、search 和 startsWith 调用次数 总计 不超过 3 * 10^4 次

解法1:字典树的基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/

//Trie是一颗非典型的多叉树模型,它和一般的多叉树不一样,尤其在结点的数据结构设计上,比如一般的多叉树的结点包括结点值、指向孩子结点的指针,而Trie的结点包括该结点是否是一个串的结束、字母映射表
class Trie {
private:

bool isEnd; //该结点是否是一个串的结束
Trie* next[26]; //字母映射表,保存了对当前结点而言下一个可能出现的所有字符的链接,因此我们可以通过一个父结点来预知它所有子结点的值

public:

Trie() {

isEnd = false;

//memset是计算机中C/C++语言初始化函数。
//作用是将某一块内存中的内容全部设置为指定的值,这个函数通常为新申请的内存做初始化工作。
//将next数组中的值全部设置为0
memset(next, 0, sizeof(next));
}

//向字典树中插入一个单词
void insert(string word) {

Trie* node = this;

for (char c : word) {
if (node->next[c-'a'] == NULL) {
node->next[c-'a'] = new Trie();
}
node = node->next[c-'a'];
}
node->isEnd = true; //要将最后一个结点isEnd = true; 表示它是一个单词的末尾
}

//判断字典树中是否存在一个单词
bool search(string word) {

Trie* node = this;

for (char c : word) {

node = node->next[c-'a'];

if (node == NULL) return false;
}
return node->isEnd;
}

//判断字典树中是否有一个以prefix开始的前缀
bool startsWith(string prefix) {

Trie* node = this;

for (char c : prefix) {

node = node->next[c-'a'];

if (node == NULL) return false;
}
return true;
}

};