字典樹(trie 樹)


字典樹(trie樹)

  字典樹是一種在字符串查找,前綴匹配等方面應用廣泛的算法,它在查找字符串時只與被查詢的字符串長度有關,所以它在查找時只有O(1)的時間復雜度,但隨之而來的較大的空間復雜度。

一.原理分析

  如圖,字典樹的每一個節點是由一個數據域(用來標記是否在此處有字符串終止)與26個指針域(表示26個小寫字母)組成(PS:聯想鏈表)。每個節點表示一個字符,我們將我們將要輸入的字符串插入字典樹中,從根節點到某一節點(具有終止標記,上圖紅點),為已插入字符串,上圖中的字符串有:abc、abcd、abd、b、bcd、efg、hig(圖片轉載字百度圖片)。

二.分步實現

1.trie樹結構體定義

 1 struct node
 2 {
 3     bool k;
 4     node *next[26];    //定義的字典樹為只有26個小寫字母,可增加減少
5 node() 6 { 7 int i; 8 for(i=0; i<26; i++) 9 next[i] = NULL; 10 k = false; 11 } 12 ~node() 13 { 14 int i; 15 for(i=0;i<26;i++) 16 { 17 if(next[i]!=NULL) 18 { 19 delete next[i]; 20 } 21 } 22 } 23 }; 24 node *head;

 

PS:被注釋掉部分是用構造函數初始化節點與使用析構函數刪除節點。
  上圖代碼中的next[26]表示每個節點的下一層節點數量,小寫字母為26,加上大寫就是52,加上數字就是62,以此類推。而bool型的K則表示該位置是否有終止標記,可根據題意改變為其他標記。

2.字典樹的插入

 1 void insert_ch(char *ch)
 2 {
 3     int i;
 4     node *p=head;
 5     for(i=0; ch[i]; i++)
 6     {
 7         if(p->next[ch[i]-'a'] == NULL)          //判斷下層節點是否存在
 8             p->next[ch[i]-'a'] = new node;  //開辟新空間
 9         p = p->next[ch[i]-'a'];                    //向下一層進行拓展
10     } 
11     p -> k = true;                                     //進行字符串結尾標記
12 }

  每次從根節點進行插入,如果向下的節點已經存在,就直接讀取,否則拓展一個新節點。之后將最后一個節點的k標記為true表示該位置有一個字符串結尾。

3.字符串查找

 1 bool find_ch(char *ch)
 2 {
 3     int i;
 4     node *p=head;
 5     for(i=0; ch[i]; i++)
 6     {
 7         if(p->next[ch[i]-'a']==NULL)
 8             return false;
 9         p = p -> next[ch[i]-'a'];
10     }
11     return p -> k;
12 }

  基本過程與插入相同,向下查找,入過該節點不存在,直接返回false,如果存在一直向下查找,最終返回末尾標記的k。

1.模板代碼

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 struct node
 8 {
 9     bool k;
10     node *next[26];
11     node()
12     {
13         int i;
14         for(i=0;i<26;i++)
15         {
16             next[i]=NULL;
17             k=false;
18         }
19     }
20     ~node()
21     {
22         int i;
23         for(i=0;i<26;i++)
24         {
25             if(next[i] != NULL)
26                 delete next[i];
27         }
28 
29     }
30 };
31 node *head;
32 void insert_ch(char *ch)
33 {
34     int i;
35     node *p = head;
36     for(i=0;ch[i];i++)
37     {
38         if(p -> next[ch[i]-'a' ] == NULL)
39             p -> next[ch[i]-'a' ] = new node;
40         p = p -> next[ch[i]-'a' ];
41     }
42     p -> k = true;
43 }
44 bool find_ch(char *ch)
45 {
46     int i;
47     node *p = head;
48     for(i=0;ch[i];i++)
49     {
50         if(p -> next[ch[i]-'a' ] == NULL)
51             return false;
52         p = p -> next[ch[i]-'a' ];
53     }
54     return p -> k;
55 }
56 char ch[100];
57 int main()
58 {
59     head = new node;
60     while(~scanf("%s",ch))
61         insert_ch(ch);
62     while(~scanf("%s",ch))
63         printf("%s\n",find_ch(ch) ? "YES" : "NO");
64     delete head;                                     //刪除節點
65     return 0;
66 }

 

三.另一種實現方式

  動態分配內存對空間掌控很好,用一個節點開辟一個節點,但是在做題中有時會出現大大小小的問題不好結決,所以可以選擇另一種方法,直接開辟出一個很大的結構體數組用來保存節點,不用考慮開辟與刪除節點問題。

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 struct node
 8 {
 9     bool k;
10     node *next[26];
11 };
12 node no[10000000],*head;
13 int iii;
14 node *new_node()
15 {
16     int i;
17     node *p=&no[iii++];
18     for(i=0;i<26;i++)
19     {
20         p -> next[i] = NULL;
21     }
22     p -> k = false;
23 }
24 void insert_ch(char *ch)
25 {
26     int i;
27     node *p=head,*t;
28     for(i=0; ch[i] ;i++ )
29     {
30         if(p->next[ch[i]-'a']==NULL)
31         {
32             t=new_node();
33             p->next[ch[i]-'a']=t;
34         }
35         p = p->next[ch[i]-'a'];
36     }
37     p -> k = true;
38 }
39 bool find_ch(char ch[])
40 {
41     int i;
42     node *p=head;
43     for(i=0;i<ch[i];i++)
44     {
45         if(p->next[ch[i]-'a']==NULL)
46             return false;
47         p = p -> next[ch[i]-'a'];
48     }
49     return p -> k;
50 }
51 int main()
52 {
53     char s[100];
54     int i,n;
55     head = new_node();
56     while(~scanf("%d",&n))
57     {
58         iii=0;
59         for(i=0;i<n;i++)
60         {
61             scanf("%s",s);
62             insert_ch(s);
63         }
64         scanf("%d",&n);
65         for(i=0;i<n;i++)
66         {
67             scanf("%s",s);
68             printf("%s\n",find_ch(s) ? "YES" : "NO");
69         }
70     }
71     return 0;
72 }

  這種方法與上面的基本相同,只是節點不在用new開辟,而是直接從結構體數組中取用,這種方法的弊端節點有最大值,當需要保存的數據量過大時,會數組越界。保存數據量小時對空間的浪費也很嚴重。

四.相關基礎題目

HDOJ 1251 統計難題:
http://acm.hdu.edu.cn/showproblem.php?pid=1251

HDOJ 1671 Phone List:
http://acm.hdu.edu.cn/showproblem.php?pid=1671    //用第一種方法記得釋放空間,否則超內存

HDOJ 1247 Hat’s Words:

http://acm.hdu.edu.cn/showproblem.php?pid=1247

 

 

PS:本貼純手打,如打碼有問題請各位大牛指正,謝謝大家~

歡迎大家轉載哦~請於此處注明出處:http://www.cnblogs.com/zhuyuan/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM