字典树的定义,就是利用字符串之间的公共前缀建树,比如说abandon和aban,这两个字符串之间有公共前缀aban,然后构建一棵树.
我们选的字典树的节点的结构表达为:
typedef struct node{
struct node *br[26];
int num;
}
分析:
(1).其中的 br[26]是对应于每个节点下面的可能有的26个字母,因为对于一个单词而言,每个字母后面的字母只有26种可能嘛(如果只是允许大写或者是小写的话).
在后面大家可能会发现,对于某个不为空的节点,它的子节点所对应的字母是按照字母表次序来的
(2).num就是指该节点的孩子节点的数目,也就是以从根节点到该节点的路径上所有字母组成的字符串为前缀的字符串的数目。
(1)定义一个全局的头指针,因为每次对字符串开始进行插入操作的时候,都需要从头结点开始,这样才可以利用公共前缀,减少存储空间,还有加快查找速度;
(2)开始建树,对字符串str,对每一次循环,如果当前指针s下面的对应于当前字符str[i]在字母表的位置的孩子节点为空,就说明此时该字符串str于其他的字符的公共前缀最多延伸到s处,
(3)新建节点,
(4)对新建节点以及新建节点的孩子进行初始化(尤其是num的初始化),如果当前指针s下面的对应于当前字符str[i]在字母表的位置的孩子结点不为空,那么此时就不需要进行新建节点,指针向下移动即可.这样对输入的每个字符串进行插入操作,就可以构建出一棵字典树了:
void Tree_Insert(char str[]){
Node *t,*s = head; //t用于中间的新建节点,s用于对头指针开始操作
int i,j;
int len = strlen(str) - 1;
for(i = 0;i <= len;i ++){
int id = str[i] - 'a';
if(s->br[id] == NULL){
t = new Node;
for(j = 0;j <= 25;j ++){
t->br[j] = NULL;
}
t->num = 0;
s->br[id] = t;
}
s = s->br[id];
s->num ++;
}
}
需要一个指针用以从头结点开始操作,也是对每个要进行查找的字符串str进行遍历,树则从头指针开始
如果当前指针s的对应于当前字符str[i]在字母表的位置的子节点不为空,那么就说明此时可以继续查找str中的下一个字符;
如果为空的话,那么就说明此时这棵树中没有以str为公共前缀的字符串,那么就可以返回为0,而对于第一种不为空的情况,每次查找都用count保存当前指针的孩子节点(不为空)的num值,最终得到的就是以str为前缀的字符串的数目;
int Tree_Find(char str[]){
Node *s = head;
int count;
int len = strlen(str) - 1;
for(int i = 0;i <= len;i ++){
int Id = str[i] - 'a';
if(s->br[Id] == NULL){
count = 0;
return count ;
}
else{
s = s->br[Id];
count = s->num;
}
}
return count;
}
大致的就是这些了。。。
hdu 1251是道蛮经典的字典树应用的问题 http://acm.hdu.edu.cn/showproblem.php?pid=1251
贴个代码
/*
* Author: kymo
* Created Time: 2011-5-26 9:20:53
* File Name:
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef struct tree
{
int num;
struct tree *br[26];
}Node;
Node *head;
void Tree_Insert(char str[]){
Node *t,*s = head;
int i,j;
int len = strlen(str) - 1;
for(i = 0;i <= len;i ++){
int id = str[i] - 'a';
if(s->br[id] == NULL){
t = new Node;
for(j = 0;j <= 25;j ++){
t->br[j] = NULL;
}
t->num = 0;
s->br[id] = t;
}
s = s->br[id];
s->num ++;
}
}