Trie树(字典树)概述
- trie树,又称字典树或前缀树,是一种有序的、用于统计、排序和存储字符串的数据结构,它与二叉查找树不同,关键字不是直接保存在结点中,而是由结点在书中的位置决定。
- 一个结点的所有子孙都有相同的前缀,也就是这个结点对应的字符串,而根结点对应空字符串。一般情况下,不是所有的结点都有对应的值,只有叶子结点和部分内部结点所对应的键才有相关的值。
- trie树的最大优点是利用字符串的公共前缀来减少存储空间与查询时间,从而最大限度地减少无谓的字符串比较,是非常高效的字符串查找数据结构。
#define TRIE_MAX_CHAR_NUM 26
struct TrieNode{
TrieNode *child[TRIE_MAX_CHAR_NUM];
bool is_end;
TrieNode():is_end(false){
for(int i=0;i<TRIE_MAX_CHAR_NUM;i++){
child[i]=0;
}
}
};
//前序遍历
void preorder_trie(TrieNode *node,int layer){
for(int i=0;i<TRIE_MAX_CHAR_NUM;i++){
if(node->child[i]!=0){
for(int j=0;j<layer;j++){
printf("---");
}
printf("%c",i+'a');
if(node->child[i].is_end==true){
printf(“(end)”);
}
printf("\n");
preorder_trie(node->child[i],layer+1);
}
}
}
Trie树获取全部单词
深度搜索trie树,对于正在搜索的结点node:
遍历该结点26个孩子指针child[i](‘a’-‘z’),如果指针不空:
将该child[i]对应的字符(i+‘a’),push进入栈中,如果该孩子指针标记的is_end为真(说明这个位置是一个单词):
从栈底到栈顶对栈进行遍历,生成字符串,将它保存到结果数组中
void get_all_word_from_trie(TrieNode *node,string &word,vector<string>&word_list){
for(int i=0;i<TRIE_MAX_CHAR_NUM;i++){
if(node->child[i]!=0){
word.push_back(i-'a');
if(node->child[i]->is_end){
word_list.push_back(word);
}
get_all_word_from_trie(node->child[i],word,word_list);
word.erase(word.length()-1,1);
}
}
}
Trie树的单词插入
使用ptr指针指向root
逐个遍历待插入的字符串中的各个字符:
计算下标pos=正在遍历字符-‘a’
如果ptr指向的结点的第pos个孩子为假:
创建该结点的第pos个孩子
ptr指向该结点的第pos个孩子
标记ptr指向的结点is_end为true
class TrieTree{
public:
TrieTree(){
}
~TrieTree(){
for(int i=0;i<_node_vec.size();i++){
delete _node_vec[i];
}
}
void insert(const char *word){
TrieNode *ptr=&_root;
while(*word){
int pos=*word-'a';
if(ptr->child[pos]==0){
ptr->child[pos]=new_node();
}
ptr=ptr->child[pos];
word++;
}
ptr->is_end=true;
}
private:
TrieNode *new_node(){
TrieNode *node=new TrieNode();
_node_vec.push_back(node);
return node;
}
vector<TrieNode *> _node_vec;
/*便于析构比较复杂的数据结构,比如树、图,析构时需要前序还是后序析构?相当复杂*/
TrieNode _root;
};
Trie树的单词搜索
使用ptr指针指向root
逐个遍历待插入的字符串中的各个字符:
计算下标pos=正在遍历字符-‘a’
如果ptr指向的结点的第pos个孩子为假:
返回假
ptr指向该结点的第pos个孩子
标记ptr指向的结点is_end
bool search(const char* word){
TrieNode *ptr= &_root;
while(*word){
int pos=*word-'a';
if(!ptr->child[pos]){
return false;
}
ptr=ptr->child[pos];
word++;;
}
return ptr->is_end;
}
Trie树的前缀查询
通常以该前缀开始有哪些单词。
该代码段只实现是否存在该前缀开始的单词。
bool startsWith(const char *prefix){
TrieNode *ptr=&_root;
while(*prefix){
int pos=*prefix-'a'