题目描述
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。
void insert(String word)
向前缀树中插入字符串 word
。
boolean search(String word)
如果字符串 word
在前缀树中,返回 true
(即,在检索之前已经插入);否则,返回 false
。
boolean startsWith(String prefix)
如果之前已经插入的字符串 word
的前缀之一为 prefix
,返回 true
;否则,返回 false
。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 输入 ["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
|
具体见题目详解。
算法思路
这个题目其实没多大意思,至少对我来说,主要考察怎么快速使用hash表,在第一次是我是正序处理字符串的,也就是对于字符串S,依次处理S[0,0], S[0,1],......,S[0,n-1]
,如果对于S,令Hash[S] = 2
,其余的Hash[prefiex] = 1
。这样可以很方便区分是insert直接得到的,还是某个insert得到的prefix。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Trie { map<string, int> treenode; public: Trie() { } void insert(string word) { string tmp = ""; for (int i = 0; i < word.length() - 1; i++) { tmp.push_back(word[i]); if(!this->treenode[tmp]){ this->treenode[tmp] = 1; } } treenode[word] = 2; } bool search(string word) { if (this->treenode[word] == 2) return true; return false; } bool startsWith(string prefix) { if (this->treenode[prefix]) return true; return false; } };
|
这样得到的结果如下:

但是这样其实有很多的浪费时间:
- 如果
Hash[S[0, n-i]] = 1
, 那么Hash[S[0, n - j]] >= 1,j >= i
恒成立,这个可以自己想一想,很简单。此时可以直接跳出循环,不再记录次数。
- 如果
Hash[word] = 2
,那么可以直接返回;如果Hash[word] = 1
,那么直接令Hash[word] = 2
,返回即可。也很容易想到是为什么,因为在之前的Insert中都记录过了。
因此我们可以将字符串倒序处理,这样就很容易化简掉很多不必要的操作。代码如下:
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
| class Trie { map<string, int> treenode; public: Trie() = default; void insert(string word) { if (this->treenode[word] == 2) { return; } if (this->treenode[word] == 1) { this->treenode[word] = 2; return; } this->treenode[word] = 2; string tmp = word; for (int i = word.size() - 1; i > 0; i--) { tmp.erase(i); if (!this->treenode[tmp]) { this->treenode[tmp] = 1; } else { break; } } } bool search(string word) { if (this->treenode[word] == 2) return true; return false; } bool startsWith(string prefix) { if (this->treenode[prefix]) return true; return false; } };
|
结果如下,优化不大, 但是我感觉再优化速度似乎对我来说没多大意义了。
