字典树是一种存储字符串的高效的结构,它保存了不同字符的相同前缀,又因此叫做前缀树,使用前缀,大大避免相同字符的重复匹配,加快查找效率
- 字典树是一颗多叉树,比如存储26个字母的,那么就有26叉
- 字典树的节点不存储字符,而是用树的边来存储字符
- 字典树的每一个节点都对应着一个字符子串
- 字典树的叶子节点对应的字符串是创建字典树的字符串集合里的元素
如图:使用 5 个字符串创建字典树,蓝色节点代表子串,红色节点表示集合里的字符串(或者说字典树的叶子节点表示完整的字符串)
["apple", "apk", "kksk", "kkp", "boki"]
结构定义
可以快速的定义一个26叉树
/*
isEnd 表示是否是叶子节点
s 表示当前节点代表的字符串
*/
class node
{
public:
bool isEnd;
string s;
node* childs[26];
node(){s=""; isEnd=false; for(int i=0; i<26; i++)childs[i]=NULL;}
};
插入元素
对于长度为n的字符串,我们依次遍历元素,从根节点开始,比如第一个字符是 ‘a’,那么查找根节点是否有 ‘a’ 的子树
- 如果有,转到那个子树
- 如果没有,创建新的子树,再转到
最后遍历结束时,记得把叶子节点的 isEnd 置一
void insert(node* &root, string s)
{
node* p = root;
for(int i=0; i<s.length(); i++)
{
if(p->childs[s[i]-'a']) p=p->childs[s[i]-'a'];
else
{
node* nd = new node();
nd->s = p->s + s[i];
p->childs[s[i]-'a'] = nd;
p = nd;
}
}
p->isEnd = true;
}
查找
查找与插入类似,从左往右遍历要查找的字符串,先找有没有对应的子树,如果有,直接转到,如果没有,返回false
遍历完成之后,记得检查当前节点的 isEnd 标志,如果为true,说明查找成功
bool search(node* root, string s)
{
node* p = root;
for(int i=0; i<s.length(); i++)
{
if(p->childs[s[i]-'a']) p=p->childs[s[i]-'a'];
else return false;
}
return p->isEnd;
}
遍历
层次遍历,没啥好说的。。
void show(node* root)
{
if(!root) return;
deque<node*> q;
q.push_back(root);
while(!q.empty())
{
int qs = q.size();
for(int i=0; i<qs; i++)
{
node* tp=q.front(); q.pop_front();
cout<<tp->s<<" ";
for(int i=0; i<26; i++)
if(tp->childs[i]) q.push_back(tp->childs[i]);
}
cout<<endl;
}
}
对字典树先序遍历可以得到所有字符串的升序排序的结果
void dlrSort(node* root)
{
if(!root) return;
if(root->isEnd) {cout<<root->s<<" "; return;}
for(int i=0; i<26; i++) dlrSort(root->childs[i]);
}
完整代码
#include <bits/stdc++.h>
using namespace std;
class node
{
public:
bool isEnd;
string s;
node* childs[26];
node(){s=""; isEnd=false; for(int i=0; i<26; i++)childs[i]=NULL;}
};
void insert(node* &root, string s)
{
node* p = root;
for(int i=0; i<s.length(); i++)
{
if(p->childs[s[i]-'a']) p=p->childs[s[i]-'a'];
else
{
node* nd = new node();
nd->s = p->s + s[i];
p->childs[s[i]-'a'] = nd;
p = nd;
}
}
p->isEnd = true;
}
bool search(node* root, string s)
{
node* p = root;
for(int i=0; i<s.length(); i++)
{
if(p->childs[s[i]-'a']) p=p->childs[s[i]-'a'];
else return false;
}
return p->isEnd;
}
void show(node* root)
{
if(!root) return;
deque<node*> q;
q.push_back(root);
while(!q.empty())
{
int qs = q.size();
for(int i=0; i<qs; i++)
{
node* tp=q.front(); q.pop_front();
cout<<tp->s<<" ";
for(int i=0; i<26; i++)
if(tp->childs[i]) q.push_back(tp->childs[i]);
}
cout<<endl;
}
}
void dlrSort(node* root)
{
if(!root) return;
if(root->isEnd) {cout<<root->s<<" "; return;}
for(int i=0; i<26; i++) dlrSort(root->childs[i]);
}
int main()
{
node* root = new node();
int t; cin>>t;
while(t--)
{
string s; cin>>s;
insert(root, s);
}
cout<<endl<<"insert finished, show tree :"<<endl;
show(root);
cout<<endl<<"end of show tree"<<endl;
cin>>t;
while(t--)
{
string s; cin>>s;
cout<<search(root, s)<<endl;
}
cout<<"sort"<<endl;
dlrSort(root);
cout<<endl;
return 0;
}
/*
sample input :
5
apple apk kksk kkp boki
*/
以上代码会产生以下结果,生成树如下: