1. 程式人生 > >字典樹演算法詳解

字典樹演算法詳解

字典樹

字典樹,又稱單詞查詢樹,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