字典樹(trie樹)簡介
其基本性質可以歸納爲:
1. 根節點不包含字符,除根節點外每一個節點都只包含一個字符。
2. 從根節點到某一節點,路徑上經過的字符連接起來,爲該節點對應的字符串。
3. 每個節點的所有子節點包含的字符都不相同。
其基本操作有:查找 插入和刪除,當然刪除操作比較少見.我在這裏只是實現了對整個樹的刪除操作,至於單個word的刪除操作也很簡單.
搜索字典項目的方法爲:
(1) 從根結點開始一次搜索;
(2) 取得要查找關鍵詞的第一個字母,並根據該字母選擇對應的子樹並轉到該子樹繼續進行檢索;
(3) 在相應的子樹上,取得要查找關鍵詞的第二個字母,並進一步選擇對應的子樹進行檢索。(4) 迭代過程……(5) 在某個結點處,關鍵詞的所有字母已被取出,則讀取附在該結點上的信息,即完成查找。其他操作類似處理.
舉個簡單的例子。
給你100000個長度不超過10的單詞。對於每一個單詞,我們要判斷他出沒出現過,如果出現了,第一次出現第幾個位置。
這題當然可以用hash來,但是我要介紹的是trie樹。在某些方面它的用途更大。比如說對於某一個單詞,我要詢問它的前綴是否出現過。這樣hash就不好搞了,而用trie還是很簡單。
現在回到例子中,如果我們用最傻的方法,對於每一個單詞,我們都要去查找它前面的單詞中是否有它。那麼這個算法的複雜度就是O(n^2)。顯然對於100000的範圍難以接受。現在我們換個思路想。假設我要查詢的單詞是abcd,那麼在他前面的單詞中,以b,c,d,f之類開頭的我顯然不必考慮。而只要找以a開頭的中是否存在abcd就可以了。同樣的,在以a開頭中的單詞中,我們只要考慮以b作爲第二個字母的……這樣一個樹的模型就漸漸清晰了……
假設有b,abc,abd,bcd,abcd,efg,hii這6個單詞,我們構建的樹就是這樣的
對於每一個節點,從根遍歷到他的過程就是一個單詞,如果這個節點被標記爲紅色,就表示這個單詞存在,否則不存在。
那麼,對於一個單詞,我只要順着他從跟走到對應的節點,再看這個節點是否被標記爲紅色就可以知道它是否出現過了。把這個節點標記爲紅色,就相當於插入了這個單詞。
這樣一來我們詢問和插入可以一起完成,所用時間僅僅爲單詞長度,在這一個樣例,便是10。
我們可以看到,trie樹每一層的節點數是26^i級別的。所以爲了節省空間。我們用動態鏈表,或者用數組來模擬動態。空間的花費,不會超過單詞數×單詞長度。
c 代碼
//=+
#include
"stdio.h"
#include "string.h"
#include "stdlib.h"
struct dictree
{
struct
dictree
*child[26];
bool
isWord=false;
int
n; //
統計一組字符串中某前綴出現的次數
}; //結點結構,有26個子節點
struct dictree
*root;
void insert (char
*source)
{
int
len,i,j;
struct
dictree
*current,*newnode;
len=strlen(source);
if(len==0)
return;
current=root;
for(i=0;i<len;i++)
{
if(current->child[source[i]-'a']!=0)
{ //插入字符 已經存在
current=current->child[source[i]-'a'];
current->n=current->n+1;
}
else
{
//不存在 新建 節點加入 字典樹中
newnode=(struct dictree *)malloc(sizeof(struct
dictree));
for(j=0;j<26;j++)
newnode->child[j]=0;
current->child[source[i]-'a']=newnode;
current=newnode;
current->n=1;
}
}
current->isWord=true;//標識 單詞的最後一個 字母爲 單詞結尾
}
int find(char
*source)
{
int
i,len;
struct
dictree
*current;