数据结构与算法之字典树解题
2013-11-30 02:35
169 查看
字典树,又称标定搜索树,是一种树形结构,也是一种哈希树的变形,典型的应用包括统计,排序和保存大量的字符串,但又不局限于字符串,还可以是数字等,所以常被搜索引擎系统用来进行词频统计,如搜索网站可用字典树进行热门搜索词的统计。它的特点是:利用字符串的公共前辍来节约存储空间,最大限度的减小字符串的比较,查询效率比哈希表高。对于大量数据而言,字典树所需要的空间相对较大,但是对于查询某个单词而言,其时间复杂度为O(n), n为字符串的长度,对于大量字符串而言,这样的查找速度是相当可观的。
字典树的树形图为:
线性结构为:
其中第一节点为根节点,不存储数据。当查询某个单词时,从根节点开始,向下遍历,如要查找hi,则h->i,时间复杂度为O(2), 如果用常规查找,即从头开始遍历,需要O(15)即所有字符串的总长度,这样的对于大量数据而言,查找效率是不现实的。 如果要查找一个不存在的字符串如kkk, 当遍历发现不存在以k开头的字符串,直接跳出,所以说查询效率是非常高的。
同时可以将大量字符数据保存到字典树中,利用字典树的公共前辍,可以大幅度地节约空间,即在字典树的一个分枝中可以保存大量有相同前辍的字符。
字典树的实现:
next表示每层有多少种类的数,如果为小写字母为26,大小写为52等等
v表示一个字典树有多少公共的前辍,这个可以根据程序需要而改变。
字典树的查找过程:
(1)每次从根节点开始一次检索;
(2)在取得字符串的第一个关键字后,根据该关键字选择对应的子树并转到该子树继续进行检索。
(3)在子树上找到第二个关键字后,并进行进一步在相应子树上进行检索。
(4)迭代过程......(若遇到找不到的关键字即结束查找,或者按需要创建节点)
(4)在某个节点上,字符串的所有关键字均找到,读取该节点上的附加信息,即完成查找。
相关使用题目:
http://198.74.100.235/wateroj/web/problem.php?id=1001
Submit: 149 Solved: 27
[Submit][Status][Web Board]
每当Water打出一个电话后,该电话号码以及以该电话号码开始的号码都永久无法被Water打出。
举个栗子,Water打出了号码12345,不管有没人接电话,下一次拨号时12345及12345*(类似1234567)的号码都无法拨出,只能听到“您拨打的用户不接你电话,请不要再拨”。
但是如果Water先拨打了1234567,下一次若为12345还是可以打出的。
Water是个锲而不舍的人,他会把他手头上的电话列表按列表顺序都打一次。
接下来N行是N个土豪的电话。电话为长度在[1,100]的数字序列。
相关题目链接:
统计难题: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/
参考博客:/article/5075800.html
字典树的树形图为:
线性结构为:
其中第一节点为根节点,不存储数据。当查询某个单词时,从根节点开始,向下遍历,如要查找hi,则h->i,时间复杂度为O(2), 如果用常规查找,即从头开始遍历,需要O(15)即所有字符串的总长度,这样的对于大量数据而言,查找效率是不现实的。 如果要查找一个不存在的字符串如kkk, 当遍历发现不存在以k开头的字符串,直接跳出,所以说查询效率是非常高的。
同时可以将大量字符数据保存到字典树中,利用字典树的公共前辍,可以大幅度地节约空间,即在字典树的一个分枝中可以保存大量有相同前辍的字符。
字典树的实现:
next表示每层有多少种类的数,如果为小写字母为26,大小写为52等等
v表示一个字典树有多少公共的前辍,这个可以根据程序需要而改变。
#define MAX 26 typedef struct Trie { Trie *next[MAX]; int v; //根据需要变化 Trie() { //初始化节点的next的各个节点为NULL for (int i = 0; i < MAX; i++) next[i] = NULL; } }; Trie *root = new Trie; //创建字典树 void createTrie(char *str) { int len = strlen(str); Trie *p = root, *q; for(int i=0; i<len; ++i) { int id = str[i]-'0'; if(p->next[id] == NULL) { q = new Trie; q->v = 1; //初始v==1 for(int j=0; j<MAX; ++j) q->next[j] = NULL; p->next[id] = q; p = p->next[id]; } else { p->next[id]->v++; p = p->next[id]; } } p->v = -1; //若为结尾,则将v改成-1表示 } //在字典树中查找字符串 int findTrie(char *str) { int len = strlen(str); Trie *p = root; for(int i=0; i<len; ++i) { int id = str[i]-'0'; p = p->next[id]; if(p == NULL) //若为空集,表示不存以此为前缀的串 return 0; if(p->v == -1) //字符集中已有串是此串的前缀 return -1; } return -1; //此串是字符集中某串的前缀 } //对于上述创建的字典树,有时会超内存,所以要释放内存 int dealTrie(Trie* T) { int i; if(T==NULL) return 0; for(i=0;i<MAX;i++) { if(T->next[i]!=NULL) deal(T->next[i]); } delete T; return 0; }
字典树的查找过程:
(1)每次从根节点开始一次检索;
(2)在取得字符串的第一个关键字后,根据该关键字选择对应的子树并转到该子树继续进行检索。
(3)在子树上找到第二个关键字后,并进行进一步在相应子树上进行检索。
(4)迭代过程......(若遇到找不到的关键字即结束查找,或者按需要创建节点)
(4)在某个节点上,字符串的所有关键字均找到,读取该节点上的附加信息,即完成查找。
相关使用题目:
http://198.74.100.235/wateroj/web/problem.php?id=1001
1001: 土豪我们做朋友吧!
Time Limit: 1 Sec Memory Limit: 16 MBSubmit: 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
实现代码:#include<stdio.h> #include<string.h> #include<iostream> using namespace std; const int MAX = 10; struct Node { int v; Node* next[MAX];//下一层字典 Node() { for (int i = 0; i < MAX; i++) next[i] = NULL; } }; Node* root = new Node; void trieTree (char* str, int& number) { int len = strlen(str); bool flag = true; Node* p = root, *q;//每次都从字典树的根节点开始遍历 for (int i = 0; i < len; i++) { int location = str[i]-'0';//可以将字符串中的数字转换为int型的数字 if (p->next[location] == NULL) { q = new Node; q->v = 1; p->next[location] = q; p = p->next[location]; } else { if (p->next[location]->v == -1) {//如果经过了该已经输入的最短号码的最后一位,则说明该输入的号码不能打通,即令flag=false flag = false; p = p->next[location];//这步很关键,因为会关系到35行的赋值,即标示最短号码的最后一位为-1 break;//跳出循环,不将该电话号码存入,节省内存,因为改题只需要判断某个电话号码可不可以打通 } p = p->next[location]; } } p->v = -1; if (flag == true)//如果未经过最短号码,则说明不是与最短号码有共同前辍的,可以打通 number++; } int main() { int phones; char* phone; int number = 0; phone = new char[101]; scanf("%d", &phones); for (int i = 0; i < phones; i++) { scanf("%s", phone); trieTree(phone, number); } printf("%d\n", number); return 0; }
相关题目链接:
统计难题: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/
参考博客:/article/5075800.html
相关文章推荐
- 数据结构与算法——提供一个单词,在字典中找到它的兄弟
- 数据结构和算法 – 6.构建字典: DictionaryBase 类和 SortedList 类
- Pku acm 1469 COURSES 数据结构题目解题报告(十一)---- 匈牙利算法求二分图的最大匹配
- 转:C#数据结构和算法学习系列十一----构建字典DictionaryBase 类和SortedList 类
- Pku acm 2239 Selecting Courses 数据结构题目解题报告(十二)---- 匈牙利算法求二分图的最大匹配
- js数据结构与算法——集合,字典,哈希
- Pku acm 1274 The Perfect Stall 数据结构题目解题报告(十三)---- 匈牙利算法求二分图的最大匹配
- Pku acm 2536 Gopher II 数据结构题目解题报告(十四)---- 匈牙利算法求二分图的最大匹配
- 【解题报告】openjudge DNA排序 数据结构与算法mooc 内排序
- Pku acm 2771 Guardian of Decency 数据结构题目解题报告(十五)---- 匈牙利算法求二分图的最大匹配
- Python cookbook(数据结构与算法)从字典中提取子集的方法示例
- Pku acm 1125 Stockbroker Grapevine 数据结构题目解题报告(八)---- 弗洛伊德(floyd)算法
- Pku acm 3041 Asteroids 数据结构题目解题报告(十六)---- 匈牙利算法求二分图的最大匹配
- 【数据结构与算法】字典代码示例
- 数据结构与算法JavaScript - 字典
- 【数据结构与算法】——排序综述
- 【数据结构与算法】最大子序列
- 数据结构与算法-链表
- Java数据结构与算法之栈_动力节点Java学院整理
- 【数据结构与算法】二叉树的层序遍历