208. Implement Trie (Prefix Tree)
这一题完全就是数据结构里树的DAT,就是类似BST/AVL一样构造Trie树。
一 概念
Trie树又被称为字典树、前缀树,是一种用于快速检索的多叉树。Tried树可以利用字符串的公共前缀来节省存储空间。但如果系统存在大量没有公共前缀的字符串,相应的Trie树将非常消耗内存。构造如下:
Trie 的强大之处就在于它的时间复杂度。它的插入和查询时间复杂度都为 O(k) ,其中 k 为 key 的长度,与 Trie 中保存了多少个元素无关。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
二 特点
它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。Trie树的基本性质可以归纳为:
(1)根节点不包含字符,除根节点意外每个节点只包含一个字符。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3)每个节点的所有子节点包含的字符串不相同。
主要操作是增删查,下面是代码实现。
三 代码实现
//字典树的实现
class TrieNode {
public:
char data;
bool isword;//该结点处是否已经是单词
TrieNode* children[26];//指向各子树的指针,最多可开26路
TrieNode(){
data=0;
isword=0;
memset(children,0,sizeof(TrieNode*)*26);
}
TrieNode(char c){
data=c;
isword=0;
memset(children,0,sizeof(TrieNode*)*26);
}
};
class Trie{
private:
TrieNode*root;
public:
Trie(){
root=new TrieNode();
}
void insert(string word){
TrieNode*p=root;
if(word.length()<1)return;
for(int i=0;i<word.length();i++){
char c=word[i];
if(p->children[c-'a']==NULL){
TrieNode*pp=new TrieNode(c);
p->children[c-'a']=pp;
}//此子树没有
p=p->children[c-'a'];//到子树
}
p->isword=1;//最后一个节点肯定是包括单词全部了
}//插入字符串
bool search(string word){
TrieNode*p=root;
if(word.length()<1)return 0;
for(int i=0;i<word.length();i++){
char c=word[i];
if(p->children[c-'a']==NULL)return 0;
p=p->children[c-'a'];
}
return p->isword;//注意不能直接返回1,这里的p可能还只是中间过程
}//查找完整字符串
bool startsWith(string prefix){
TrieNode*p=root;
if(prefix.length()<1)return 1;//空也是匹配前缀
for(int i=0;i<prefix.length();i++){
char c=prefix[i];
if(p->children[c-'a']==NULL)return 0;
p=p->children[c-'a'];
}
return 1;//注意这里求前缀匹配直接返回1
}//匹配前缀
void frees(TrieNode* proot){
if(proot==NULL)return;
for(int i=0;i<26;i++)
{
TrieNode*p=proot->children[i];
if(p)frees(p);//递归删除p路子树
}
delete proot;
}//删除proot所指树
~Trie(){
frees(root);
}
};
Trie树的应用:
211. Add and Search Word - Data structure design
Design a data structure that supports the following two operations:
void addWord(word) bool search(word)
search(word) can search a literal word or a regular expression string containing only letters a-z
or .
. A .
means it can represent any one letter.
For example:
addWord("bad") addWord("dad") addWord("mad") search("pad") -> false search("bad") -> true search(".ad") -> true search("b..") -> true要用到字典树的结构,唯一不同的地方就是search的函数需要重新写一下,因为这道题里面'.'可以代替任意字符,所以一旦有了'.',就需要查找所有的子树,只要有一个返回true,整个search函数就返回true
代码:
class TrieNode{
public:
char data;
bool isword;
TrieNode*child[26];
TrieNode(){
data=0;
isword=0;
memset(child,0,sizeof(TrieNode*)*26);
}
TrieNode(char c)
{data=c;
isword=0;
memset(child,0,sizeof(TrieNode*)*26);
}
};
class WordDictionary{
private:
TrieNode *root;
public:
WordDictionary(){root=new TrieNode();}/** Initialize your data structure here. */
void addWord(string word){
TrieNode*p=root;
if(word.length()<1)return;
for(int i=0;i<word.length();i++){
char c=word[i];
if(p->child[c-'a']==NULL)
{
TrieNode*pp=new TrieNode(c);
p->child[c-'a']=pp;
}
p=p->child[c-'a'];
}
p->isword=1;
} /** Adds a word into the data structure. */
bool search(string word,TrieNode*r,int i){
if(i==word.size())return r->isword;
char c=word[i];
if(c=='.'){
for(int j=0;j<26;j++)
if(r->child[j]&&search(word,r->child[j],i+1))return 1;
}
else {
if(r->child[c-'a']==NULL)return 0;
else return search(word,r->child[c-'a'],i+1);
}
return 0;
}
bool search(string word){
if(word.size()<1)return 0;
return search(word,root,0);
}
/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
};
/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary obj = new WordDictionary();
* obj.addWord(word);
* bool param_2 = obj.search(word);
*/