前缀树的好处是:
虽然散列表能够查询单词是否是有效的,但是他不能去识别字符串中是否有任何有效单词的前缀。而前缀树可以很快地做到这一点。
前缀树的实现:
实际上主要是因为之前没想到要这样子来存储数据,一开始按照网上看得图是这样子的,(图片来自Leetcode https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/trie-tree-de-shi-xian-gua-he-chu-xue-zhe-by-huwt/ 这位大佬的)
如果按照上图的来做,怎么都想不出来是什么结构,直到看到了下面这个图片。
所以实际上前缀树是一个N叉树,另外的话它的操作非常类似于链表的操作。因为总是从根节点开始找,然后一直往下。
class Trie {
public:
bool isEnd;
Trie* next[26];
/** Initialize your data structure here. */
Trie() {
isEnd = false;
memset(next,0,sizeof(next));
}
/** Inserts a word into the trie. */
void insert(string word) {
Trie* curnode = this;//答案的这种写法很骚
for (auto ch : word){
if(curnode->next[ch-'a'] == NULL){
curnode->next[ch- 'a'] = new Trie();
}
curnode = curnode->next[ch-'a'];
}
curnode->isEnd = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Trie* curnode = this;
for(auto ch : word){
if(curnode->next[ch - 'a'] == NULL){
return false;
}
curnode = curnode->next[ch-'a'];//找到了那个节点了
}
return curnode->isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Trie* curnode = this;
for(auto ch : prefix){
if(curnode->next[ch - 'a'] == NULL){
return false;
}
curnode = curnode->next[ch-'a'];//找到了那个节点了
}
return true;
}
};
上面的实现是一个简单的实现方法,实际上前缀树还可以支持更多的操作,比如整棵树的销毁,含有某种前缀的单词有多少个,以及删除某个单词上,删除操作过于麻烦..。所以我们需要重新定义Trie树的顶点设计
介绍一下销毁的函数
void Destrory(TrieNode* root){
if(root == NULL){
return;
}
for(int i = 0 ; i < 26 ; i++){
if(root->next[i]!=NULL){
Destrory(root->root[i]);
}
}
delete root;
root = NULL;
return;
}