字典樹(前綴樹)

本文轉載自:https://blog.csdn.net/weixin_39778570/article/details/81990417

什麼是字典樹?

 

叫前綴樹更容易理解

字典樹的樣子

這裏寫圖片描述

Trie又被稱爲前綴樹、字典樹,所以當然是一棵樹。上面這棵Trie樹包含的字符串集合是{in, inn, int, tea, ten, to}。每個節點的編號是我們爲了描述方便加上去的。樹中的每一條邊上都標識有一個字符。這些字符可以是任意一個字符集中的字符。比如對於都是小寫字母的字符串,字符集就是’a’-‘z’;對於都是數字的字符串,字符集就是’0’-‘9’;對於二進制字符串,字符集就是0和1。

比如上圖中3號節點對應的路徑0123上的字符串是inn,8號節點對應的路徑0568上的字符串是ten。終結點與集合中的字符串是一一對應的。

原理


下面我們來講一下對於給定的字符串集合{W1, W2, W3, … WN}如何創建對應的Trie樹。其實上Trie樹的創建是從只有根節點開始,通過依次將W1, W2, W3, … WN插入Trie中實現的。所以關鍵就是之前提到的Trie的插入操作。
具體來說,Trie一般支持兩個操作:
1. Trie.insert(W):第一個操作是插入操作,就是將一個字符串W加入到集合中。
2. Trie.search(S):第二個操作是查詢操作,就是查詢一個字符串S是不是在集合中。

假設我們要插入字符串”in”。我們一開始位於根,也就是0號節點,我們用P=0表示。我們先看P是不是有一條標識着i的連向子節點的邊。沒有這條邊,於是我們就新建一個節點,也就是1號節點,然後把1號節點設置爲P也就是0號節點的子節點,並且將邊標識爲i。最後我們移動到1號節點,也就是令P=1。

這裏寫圖片描述

這樣我們就把”in”的i字符插入到Trie中了。然後我們再插入字符n,也是先找P也就是1號節點有沒有標記爲n的邊。還是沒有,於是再新建一個節點2,設置爲P也就是1號節點的子節點,並且把邊標識爲n。最後再移動到P=2。這樣我們就把n也插入了。由於n是”in”的最後一個字符,所以我們還需要將P=2這個節點標記爲終結點。

這裏寫圖片描述

現在我們再插入字符串”inn”。過程也是一樣的,從P=0開始找標識爲i的邊,這次找到1號節點。於是我們就不用創建新節點了,直接移動到1號節點,也就是令P=1。再插入字符n,也是有2號節點存在,所以移動到2號節點,P=2。最後再插入字符n這時P沒有標識爲n的邊了,所以新建3號節點作爲2號節點的子節點,邊標識爲n,同時將3號節點標記爲終結點:

這裏寫圖片描述
將後面的字符串int tea ten to都插入之後,就得到了我們一開始給出的Trie:

這裏寫圖片描述

綜上所述,在Trie中插入一個字符串W的僞代碼如下:

這裏寫圖片描述
下面我們再講一下如何查詢Trie樹中是不是包含字符串S,也就是之前提到的查找操作。查找其實比較簡單。我們只要從根節點開始,沿着標識着S[1] -> S[2] -> S[3] … -> S[S.len]的邊移動,如果最後成功到達一個終結點,就說明S在Trie樹中;如果最後無路可走,或者到達一個不是終結點的節點,就說明S不在Trie樹中。

這裏寫圖片描述

如果是查找”te”,就會從0開始經過5最後到達6。但是6不是終結點,所以te沒在Trie樹中。如果查找的是”too”,就會從0開始經過5和9,然後發現之後無路可走:9號節點沒有標記爲o的邊連出去。所以too也不在Trie中。

綜上所述,在Trie樹中查找一個字符串的僞代碼如下:

這裏寫圖片描述
代碼實現

數組方式實現
要寫代碼實現一個Trie首先就要確定如何存儲一個Trie結構。這裏用一個二維數組來存儲:

int trie[MAX_NODE][CHARSET];
int k;
其中MAX_NODE是trie中最大能存儲的節點數目,CHARSET是字符集的大小,k是當前trie中包含有多少個節點。Trie[i][j]的值是0表示trie樹中i號節點,並沒有一條連出去的邊,滿足邊上的字符標識是字符集中第j個字符(從0開始);trie[i][j]的值是正整數x表示trie樹中i號節點,有一條連出去的邊,滿足邊上的字符標識是字符集中第j個字符,並且這條邊的終點是x號節點。

簡要實現:

#include <bits/stdc++.h>
typedef long long ll;
const int maxx=10010;
const int maxn=26;
const int inf=0x3f3f3f3f;
using namespace std;
int Trie[maxx][maxn]= {0}; //maxx表示Trie中最大能儲存的節點數目,maxn表示字符集的大小
int k=1;//k是Trie中包含了多少個節點。
int color[maxx]= {0};
void insert(char *w)
{
    int len=strlen(w);
    int p=0;//根結點
    for(int i=1; i<=len; i++)
    {
        int c=w[i]-'a';
        if(Trie[p][c]==0)//如果沒有標識爲w[i]的邊
        {
            Trie[p][c]=k;//創建一個新的子節點
            k++;
        }
        p=Trie[p][c];
    }
    color[p]=1;//標記p爲終結點
}
int search(char *s)
{
    int len=strlen(s);
    int p=0;
    for(int i=1; i<=len; i++)
    {
        int c=s[i]-'a';
        if(Trie[p][c]==0)
            return 0;
        p=Trie[p][c];
    }
    return color[p]==1;//標記p爲終結點
}
int main()
{
    int t,q;
    char s[20];
    scanf("%d%d",&t,&q);
    while(t--)
    {
        scanf("%s",s);
        insert(s);
    }
    while(q--)
    {
        scanf("%s",s);
        if(search(s)!=0)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

 

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