其实这个拖了很久了,今天总算有机会把字典树的实现自己写一遍了,毕竟这么好的数据结构放着不用确实有些可惜。一次建树,多次查询。
和普通的树结构不一样,一般的树结构都是
1 2 3 4 5
| template<class T> struct TreeNode{ T value; TreeNode *children[NUM]; };
|
但是字典树的结构是这样的
1 2 3 4
| struct TrieNode{ bool isENnd; TrieNode* next[26]; };
|
大概意思就是说,我们可以通过父节点来得知他所有子节点的值,如果子节点中存在该字符,那么节点就不为空。
通过代码来讲述功能可能更为合适。
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
| clas Trie{ private: bool isEnd; Trie* next[26]; public: Trie(){ isEnd = false; memset(next,nullptr,sizeof(next)); }
void insert(string word){ Trie* node = this; for(char& c: word){ if(node->next[c-'a']==nullptr){ node->next[c-'a'] = new Trie(); } node = node->next[c-'a']; } node->isEnd = true; }
bool search(string& word){ Trie* node = this; for(char& c:word){ if(node->next[c-'a']==nullptr){ return false; } node = node->next[c-'a']; } return node->isEnd; }
bool startWith(string& prefix){ Trie* node = this; for(char& c:prefix){ if(node->next[c-'a']==nullptr){ return false; } node = node->next[c-'a']; } return true; } }
|
插入过程其实就是从头结点开始遍历,如果当前单词中的字母在字典树中未出现,则新建相应的节点。另外就是查找和检查前缀,两者的区别主要在于前缀匹配不需要检查最后的节点是否是字符结尾。
总结
好像也没啥总结的,算是完成了之前的一个遗留任务吧,吐槽一句,work life balance真的很重要,今天算是半年以来第一次打球,体力真的差好多。。。