lintcode-442-实现 Trie
442-实现 Trie
实现一个 Trie,包含 insert, search, 和 startsWith 这三个方法。
注意事项
你可以假设所有的输入都是小写字母a-z。
样例
insert("lintcode")
search("code") // return false
startsWith("lint") // return true
startsWith("linterror") // return false
insert("linterror")
search("lintcode) // return true
startsWith("linterror") // return true标签
字典树 脸书 谷歌 优步
思路
字典树(Trie)可以保存一些 字符串->值 的对应关系。基本上,它跟 Java 的 HashMap 功能相同,都是 key-value 映射,只不过 Trie 的 key 只能是字符串。
Trie 的强大之处就在于它的时间复杂度。它的插入和查询时间复杂度都为 O(k) ,其中 k 为 key 的长度,与 Trie 中保存了多少个元素无关。Hash 表号称是 O(1) 的,但在计算 hash 的时候就肯定会是 O(k) ,而且还有碰撞之类的问题;Trie 的缺点是空间消耗很高。
Trie树的基本性质可以归纳为:
- 根节点不包含字符,除根节点意外每个节点只包含一个字符。
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符串不相同。
Trie树有一些特性:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
- 如果字符的种数为n,则每个结点的出度为n,这也是空间换时间的体现,浪费了很多的空间。
- 插入查找的复杂度为O(n),n为字符串长度。
基本思想(以字母树为例):
1、插入过程
对于一个单词,从根开始,沿着单词的各个字母所对应的树中的节点分支向下走,直到单词遍历完,将最后的节点标记为红色,表示该单词已插入Trie树。
2、查询过程
同样的,从根开始按照单词的字母顺序向下遍历trie树,一旦发现某个节点标记不存在或者单词遍历完成而最后的节点未标记为红色,则表示该单词不存在,若最后的节点标记为红色,表示该单词存在。
code
/**
* Your Trie object will be instantiated and called as such:
* Trie trie;
* trie.insert("lintcode");
* trie.search("lint"); will return false
* trie.startsWith("lint"); will return true
*/
class TrieNode {
public:
// Initialize your data structure here.
char c;
TrieNode * next[26];
bool isEnd;
TrieNode() {
c = ' ';
for (int i = 0; i < 26; i++) {
next[i] = nullptr;
}
isEnd = false;
}
TrieNode(char c) {
this->c = c;
for (int i = 0; i < 26; i++) {
next[i] = nullptr;
}
isEnd = false;
}
};
class Trie {
public:
Trie() {
root = new TrieNode();
}
// Inserts a word into the trie.
void insert(string word) {
TrieNode* curNode = root;
for (int i = 0; i < word.size(); i++) {
if (curNode->next[word[i] - 'a'] != nullptr) {
curNode = curNode->next[word[i] - 'a'];
}
else {
TrieNode* node = new TrieNode(word[i]);
curNode->next[word[i] - 'a'] = node;
curNode = node;
}
}
curNode->isEnd = true;
}
// Returns if the word is in the trie.
bool search(string word) {
TrieNode* curNode = root;
for (int i = 0; i < word.size(); i++) {
if (curNode->next[word[i] - 'a'] == nullptr) {
return false;
}
else {
curNode = curNode->next[word[i] - 'a'];
}
}
return curNode->isEnd;
}
// Returns if there is any word in the trie
// that starts with the given prefix.
bool startsWith(string prefix) {
TrieNode* curNode = root;
for (int i = 0; i < prefix.size(); i++) {
if (curNode->next[prefix[i] - 'a'] == nullptr) {
return false;
}
else {
curNode = curNode->next[prefix[i] - 'a'];
}
}
return true;
}
private:
TrieNode* root;
};