1. 基本概念:
Trie,又称前缀树或字典树,是一棵有根树,其每个节点包含以下字段:
指向子节点的指针数组next[26],其中next[0]对应小写字母'a',next[1]对应小写字母'b'……
bool isEnd,表示该节点是否为单词的结尾。
ps:图片来源于网络
2. 插入、查找(或者查找前缀)、删除操作的代码
以下代码为26个小写字母组成的字典树
class Trie{
bool isEnd; //是否有以该字母为结尾的单词
Trie *next[26];
public:
Trie(){
isEnd=false;
for(int i=0;i<26;i++) next[i]=nullptr;
}
void insert(string word){ //插入一个新单词
Trie *node=this;
for(char c:word){
if(!node->next[c-'a']) 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']) return false;
node=node->next[c-'a'];
}
return node->isEnd; //如果只查询是否存在prefix,则直接返回true即可
}
void del(string word){ //删除该字符
stack<Trie*> s;
Trie *node=this;
for(char c:word){
if(!node->next[c-'a']) return; //没这个单词,直接返回
node=node->next[c-'a'];
s.push(node);
}
if(!node->isEnd) return; //没这个单词,直接返回
if(!isLeaf(node)){ //如果该结点不是叶子结点
node->isEnd=false; //把isEnd置成false即可
return;
}
delete node;
s.pop();
while(!s.empty()){
node=s.top();
s.pop();
if(!node->isEnd&&isLeaf(node)) delete node; //只能删除不以该词为结尾的叶子结点
else break;
}
}
bool isLeaf(Trie*node){ //判断是否为叶子结点,用于删除操作
for(auto x:next){
if(x) return false;
}
return true;
}
};
3. 复杂度分析
时间复杂度:初始化为O(1),插入和查找为O(S),其中S是每次插入或查找的字符串长度
空间复杂度:O(T*λ),其中T为所有插入字符串的长度之和,λ为字符集的大小,该代码中为26。