C++實現Trie字典樹
阿新 • • 發佈:2019-02-02
一、前言
這篇文章來源於HihoCode的#1014題,此處為連結
二、Trie字典樹
字典樹,顧名思義就是將單詞存在一個樹結構中,用來對單詞進行儲存和查詢功能,並且可以統計出以某一特定字串為字首共有多少單詞,本文中只涉及英文單詞。
字典樹是一個26叉樹,每一個父節點都有26個子節點,分別對應了26個英文字母,樹的結構大致如下:
理所當然的根節點不代表任何字母,作為一個空節點。那麼每個節點應當具有什麼樣的結構呢?
三、節點結構
節點中應當包含一個指向26個子節點的指標陣列,為了統計以某一字串為字首的單詞出現了多少次,節點中應當有此節點儲存了多少次的計數值,並且應當儲存下當前節點代表的字元。
四、針對題目的分析class TrieNode{ public: int count; TrieNode * nextNode[26]; char *word; public: TrieNode():word(NULL),count(0){ for(int i=0;i<26;i++){ nextNode[i]=NULL; } } };
題目中要求先將給出的單詞存入樹中,這就要求我們的樹需要有插入操作。插入操作可以通過遞迴和迴圈來實現,在這裡我們介紹迴圈的方式。
每次插入一個單詞時我們需要將對應的字串的每一位提取出來,判斷這個字元是否為合法的英文字元,並且在當前節點的子節點中找到這個字元對應的子節點。如果這個子節點不為空,說明之前已經插入過一次,那麼這次插入只需要更新它的count計數,否則的話我們需要初始化這個節點,並且將它對應的字元和count計數值都更新。
當所有的單詞都插入後,我們需要對給定的前綴出現的次數進行統計,也就是我們要找到這個字首的最後一個字元所在的節點的count計數值。因此從根節點開始層層尋找,如果沒有到這個字首的字元的最後一位就遇到了空節點,說明字典中並沒有存放這個字首單詞,需要返回零。如果找到了最後一個字元所在的節點,那麼就返回它的計數值。void insert(string str){ int index; TrieNode *tmp=root; tmp->count++; for(int i=0;i<str.size();i++){ index=str[i]-'a'; if(index < 0 || index > 25) return; else if(tmp->nextNode[index]==NULL){ tmp->nextNode[index]=new TrieNode(); //tmp->nextNode[index]->word } tmp=tmp->nextNode[index]; tmp->count++; tmp->word[0]=str[i]; } }
這兩個方法已經足夠我們應付這道題目了,下面來介紹一下方便我們除錯用的一個函式——列印整棵樹。bool isFound(const string str,int & cnt){ int i=0; int index=-1; TrieNode *tmp=root; for(;i<str.size();i++){ index=str[i]-'a'; if(0>index || index>25) {cnt=0;return false;} tmp=tmp->nextNode[index]; if(tmp!=NULL) cnt=tmp->count; else {cnt=0;return false;} } return true; }
列印整棵樹其實就是遍歷整棵樹,我們為了方便就採取前序遍歷,遍歷採用簡單的遞迴。
void print(TrieNode * tmp){
if(tmp==NULL) return;
else{
cout<<tmp->word[0]<<"\t";
}
for(int i=0;i<26;i++)
print(tmp->nextNode[i]);
}
void printAll(){
print(root);
}
五、完整程式碼
這裡貼出完整的程式碼供大家參考。
#include <iostream>
#include <string>
using namespace std;
class TrieNode{
public:
int count;
TrieNode * nextNode[26];
char *word;
public:
TrieNode():word(NULL),count(0){
for(int i=0;i<26;i++){
nextNode[i]=NULL;
}
}
};
class TrieTree{
public:
TrieNode * root;
TrieTree(){
root=new TrieNode();
}
~TrieTree(){
delete [] root;
}
void insert(string str){
int index;
TrieNode *tmp=root;
tmp->count++;
for(int i=0;i<str.size();i++){
index=str[i]-'a';
if(index < 0 || index > 25) return;
else if(tmp->nextNode[index]==NULL){
tmp->nextNode[index]=new TrieNode();
//tmp->nextNode[index]->word
}
tmp=tmp->nextNode[index];
tmp->count++;
tmp->word[0]=str[i];
}
}
bool isFound(const string str,int & cnt){
int i=0;
int index=-1;
TrieNode *tmp=root;
for(;i<str.size();i++){
index=str[i]-'a';
if(0>index || index>25) {cnt=0;return false;}
tmp=tmp->nextNode[index];
if(tmp!=NULL) cnt=tmp->count;
else {cnt=0;return false;}
}
return true;
}
void print(TrieNode * tmp){
if(tmp==NULL) return;
else{
cout<<tmp->word[0]<<"\t";
}
for(int i=0;i<26;i++)
print(tmp->nextNode[i]);
}
void printAll(){
print(root);
}
};
int N;
int S;
int main(){
TrieTree t;
int cnt=0;
string str;
cin>>N;
int i;
int *a;
for(i=0;i<N;i++){
cin>>str;
//cout<<"hi"<<endl;
t.insert(str);
}
t.printAll();
cin>>S;
a=new int[S];
for(i=0;i<S;i++){
cnt=0;
cin>>str;
t.isFound(str,cnt);
a[i]=cnt;
}
for(i=0;i<S;i++){
cout<<a[i]<<endl;
}
return 0;
}