数据结构与算法之字典树解题

字典树,又称标定搜索树,是一种树形结构,也是一种哈希树的变形,典型的应用包括统计,排序和保存大量的字符串,但又不局限于字符串,还可以是数字等,所以常被搜索引擎系统用来进行词频统计,如搜索网站可用字典树进行热门搜索词的统计。它的特点是:利用字符串的公共前辍来节约存储空间,最大限度的减小字符串的比较,查询效率比哈希表高。对于大量数据而言,字典树所需要的空间相对较大,但是对于查询某个单词而言,其时间复杂度为O(n), n为字符串的长度,对于大量字符串而言,这样的查找速度是相当可观的。

字典树的树形图为:

线性结构为:

其中第一节点为根节点,不存储数据。当查询某个单词时,从根节点开始,向下遍历,如要查找hi,则h->i,时间复杂度为O(2), 如果用常规查找,即从头开始遍历,需要O(15)即所有字符串的总长度,这样的对于大量数据而言,查找效率是不现实的。 如果要查找一个不存在的字符串如kkk, 当遍历发现不存在以k开头的字符串,直接跳出,所以说查询效率是非常高的。

同时可以将大量字符数据保存到字典树中,利用字典树的公共前辍,可以大幅度地节约空间,即在字典树的一个分枝中可以保存大量有相同前辍的字符。

字典树的实现:

next表示每层有多少种类的数,如果为小写字母为26,大小写为52等等

v表示一个字典树有多少公共的前辍,这个可以根据程序需要而改变。

 1 #define MAX 26
 2 typedef struct Trie   
 3 {   
 4     Trie *next[MAX];   
 5     int v;   //根据需要变化
 6     Trie() { //初始化节点的next的各个节点为NULL 
 7         for (int i = 0; i < MAX; i++)
 8             next[i] = NULL;
 9     } 
10 };   
11  
12 Trie *root = new Trie;
13 //创建字典树 
14 void createTrie(char *str)
15 {
16     int len = strlen(str);
17     Trie *p = root, *q;
18     for(int i=0; i<len; ++i)
19     {
20         int id = str[i]-'0';
21         if(p->next[id] == NULL)
22         {
23             q = new Trie;
24             q->v = 1;    //初始v==1
25             for(int j=0; j<MAX; ++j)
26                 q->next[j] = NULL;
27             p->next[id] = q;
28             p = p->next[id];
29         }
30         else
31         {
32             p->next[id]->v++;
33             p = p->next[id];
34         }
35     }
36     p->v = -1;   //若为结尾,则将v改成-1表示
37 }
38 //在字典树中查找字符串 
39 int findTrie(char *str)
40 {
41     int len = strlen(str);
42     Trie *p = root;
43     for(int i=0; i<len; ++i)
44     {
45         int id = str[i]-'0';
46         p = p->next[id];
47         if(p == NULL)   //若为空集,表示不存以此为前缀的串
48             return 0;
49         if(p->v == -1)   //字符集中已有串是此串的前缀
50             return -1;
51     }
52     return -1;   //此串是字符集中某串的前缀
53 }
54 //对于上述创建的字典树,有时会超内存,所以要释放内存 
55 int dealTrie(Trie* T)
56 {
57     int i;
58     if(T==NULL)
59         return 0;
60     for(i=0;i<MAX;i++)
61     {
62         if(T->next[i]!=NULL)
63             deal(T->next[i]);
64     }
65     delete T;
66     return 0;
67 }

字典树的查找过程:

(1)每次从根节点开始一次检索;

(2)在取得字符串的第一个关键字后,根据该关键字选择对应的子树并转到该子树继续进行检索。

(3)在子树上找到第二个关键字后,并进行进一步在相应子树上进行检索。

(4)迭代过程......(若遇到找不到的关键字即结束查找,或者按需要创建节点)

(4)在某个节点上,字符串的所有关键字均找到,读取该节点上的附加信息,即完成查找。

相关使用题目:

1001: 土豪我们做朋友吧!

Time Limit: 1 Sec  Memory Limit: 16 MB
Submit: 149  Solved: 27
[Submit][Status][Web Board]

Description

Water最近掌握了一大波土豪的电话,他决定一个个打电话去找土豪做朋友。可是土豪们不堪骚扰,于是联合通信公司对Water的电话做了一个封杀规则。
每当Water打出一个电话后,该电话号码以及以该电话号码开始的号码都永久无法被Water打出。
举个栗子,Water打出了号码12345,不管有没人接电话,下一次拨号时12345及12345*(类似1234567)的号码都无法拨出,只能听到“您拨打的用户不接你电话,请不要再拨”。
但是如果Water先拨打了1234567,下一次若为12345还是可以打出的。
Water是个锲而不舍的人,他会把他手头上的电话列表按列表顺序都打一次。

Input

第一行为N [1,1 000 000]。

接下来N行是N个土豪的电话。电话为长度在[1,100]的数字序列。

Output

一个整数M,代表Water成功骚扰到的土豪的个数。(即没有被封杀的电话个数)

Sample Input

5
123
12
12345
123
23451

Sample Output

3

HINT

实现代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 using namespace std;
 5 const int MAX = 10; 
 6 struct Node {
 7      int v;
 8      Node* next[MAX];//下一层字典 
 9      Node() {
10         for (int i = 0; i < MAX; i++)
11             next[i] = NULL;
12      }
13 };
14 Node* root = new Node;
15 void trieTree (char* str, int& number) {
16      int len = strlen(str);
17      bool flag = true;
18      Node* p = root, *q;//每次都从字典树的根节点开始遍历 
19      for (int i = 0; i < len; i++) {
20          int location = str[i]-'0';//可以将字符串中的数字转换为int型的数字 
21          if (p->next[location] == NULL) {
22              q = new Node;
23              q->v = 1;
24              p->next[location] = q;
25              p = p->next[location];
26          } else {
27             if (p->next[location]->v == -1) {//如果经过了该已经输入的最短号码的最后一位,则说明该输入的号码不能打通,即令flag=false 
28                  flag = false;
29                  p = p->next[location];//这步很关键,因为会关系到35行的赋值,即标示最短号码的最后一位为-1 
30                  break;//跳出循环,不将该电话号码存入,节省内存,因为改题只需要判断某个电话号码可不可以打通 
31              }
32              p = p->next[location];
33          }
34      }
35      p->v = -1;
36      if (flag == true)//如果未经过最短号码,则说明不是与最短号码有共同前辍的,可以打通 
37         number++;
38 }
39 int main() {
40     int phones;
41     char* phone;
42     int number = 0;
43     phone = new char[101];
44     scanf("%d", &phones);
45     for (int i = 0; i < phones; i++) {
46         scanf("%s", phone);
47         trieTree(phone, number);
48     }
49     printf("%d\n", number);
50     return 0;
51 } 
52          

相关题目链接:

统计难题:http://acm.hdu.edu.cn/showproblem.php?pid=1251

Phone List: http://acm.hdu.edu.cn/showproblem.php?pid=1671

字典树资料:算法合集之《浅析字母树在信息学竞赛中的应用》

http://www.rayfile.com/zh-cn/files/cb23e411-c735-11df-934c-0015c55db73d/

参考博客:http://www.cnblogs.com/tanky_woo/archive/2010/09/24/1833717.html

posted @ 2013-11-30 02:35  XYZ篮球  阅读(769)  评论(0编辑  收藏  举报