字典樹演算法詳解
字典樹
字典樹,又稱單詞查詢樹,Trie樹,是一種樹形結構,雜湊表的一個變種。用於統計,排序和儲存大量的字串(也可以儲存其
的)。
優點就是利用公共的字首來節約儲存空間。在這舉個簡單的例子:比如說我們想儲存3個單詞,nyist、nyistacm、nyisttc。如果只是
單純的按照以前的字元陣列儲存的思路來儲存的話,那麼我們需要定義三個字串陣列。但是如果我們用字典樹的話,只需要定義
一個樹就可以了。在這裡我們就可以看到字典樹的優勢了。
基本的操作
1.定義(即定義結點)
struct node{ int cnt; struct node *next[26]; node(){ cnt=0; memset(next,0,sizeof(next)); } };
next是表示每層有多少種類的數,如果只是小寫字母,則26即可,若改為大小寫字母,則是52,若再加上數字,則是62了,這裡根
據題意來確定。
cnt可以表示一個字典樹到此有多少相同字首的數目,這裡根據需要應當學會自由變化。
2.插入(即建樹過程)
構建Trie樹的基本演算法也很簡單,無非是逐一把每則單詞的每個字母插入Trie。插入前先看字首是否存在。如果存在,就共享,否則
建立對應的節點和邊。比如要插入單詞add(已經插入了單詞“ad”),就有下面幾步:
考察字首"a",發現邊a已經存在。於是順著邊a走到節點a。
考察剩下的字串"dd"的字首"d",發現從節點a出發,已經有邊d存在。於是順著邊d走到節點ad
考察最後一個字元"d",這下從節點ad出發沒有邊d了,於是建立節點ad的子節點add,並把邊ad->add標記為d。
void buildtrie(char *s){ node *p = root; node *tmp = NULL; int l = strlen(s); for(int i = 0; i < l; ++i){ if(p->next[s[i]-'a'] == NULL){ tmp = new node; p->next[s[i]-'a'] = tmp; } p = p->next[s[i]-'a']; p->cnt++; } }
3.查詢
(1)每次從根結點開始進行搜尋;
(2)取要查詢關鍵詞的第一個字母,並根據該字母選擇對應的子樹並轉到該子樹繼續進行檢索;
(3)在相應的子樹上,取得要查詢關鍵詞的第二個字母,並進一步選擇對應的子樹進行檢索;
(4)迭代剛才過程。。。
(5)直到在某個結點處:
——關鍵詞的所有字母都被取出,則讀取附在該結點上的資訊,即完成查詢。
——該結點沒有任何資訊,則輸出該關鍵詞不在此字典樹裡。
void findtrie(char *s){
node *p = root;
int l = strlen(s);
for(int i = 0; i < l; ++i){
if(p->next[s[i]-'a'] == NULL){
printf("0\n");
return;
}
p = p->next[s[i]-'a'];
}
printf("%d\n",p->cnt);
}
4.釋放記憶體
有些題目,資料比較大,需要查詢完之後釋放記憶體(比如:hdu1671 Phone List)
遞迴釋放記憶體:
void del(node *root){
for(int i = 0; i < 10; ++i)
if(root->next[i])
del(root->next[i]);
delete(root);
}
注意事項:
1.用G++交會出現Memory Limit Exceeded(就算釋放了記憶體,還是Memory Limit Exceeded)
2.根結點要初始化root=new node;
練習:
hdu 1251 統計難題
hdu 2072 單詞數
hdu 1671 Phone List
POJ 2001 Shortest Prefixes
POJ 2418 Hardwood Species
POJ 2503 Babelfish