字符串 之字典樹 算法

字典樹(trie樹)簡介

Trie樹|字典樹的簡介Trie,又稱字典樹、單詞查找樹,是一種樹形結構,用於保存大量的字符串,其核心思想是空間換時間。它的優點是:利用字符串的公共前綴來節約存儲空間。相對來說,Trie樹是一種比較簡單的數據結構.理解起來比較簡單,正所謂簡單的東西也得付出代價.故Trie樹也有它的缺點,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個單詞,我們構建的樹就是這樣的

字符串 <wbr>之字典樹 <wbr>算法

對於每一個節點,從根遍歷到他的過程就是一個單詞,如果這個節點被標記爲紅色,就表示這個單詞存在,否則不存在。
那麼,對於一個單詞,我只要順着他從跟走到對應的節點,再看這個節點是否被標記爲紅色就可以知道它是否出現過了。把這個節點標記爲紅色,就相當於插入了這個單詞。
這樣一來我們詢問和插入可以一起完成,所用時間僅僅爲單詞長度,在這一個樣例,便是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; 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章