这个由来是我写了KMP,然后看了AC自动机,别人提到AC自动机还需要字典树,所以我就看了。(给自己提个醒勤于复习,还有 先生你还没看 KMP的优化算法 )
那~我学习字典树的时候恰好也在学习数据结构树这一章节所以接受起来还是蛮快的,我瞧得不好,也不快,我比较期待学习哈希表和图的 ,尤其是图 因为之前建模的时候有过图的题,那时候还没学离散数学的图论,很瞎。学校其实也没有细细的将数据结构。唉不说了,每个人都有自己的经历;
这个树很多东西,像什么前缀后缀我都只是了解了还有细看;
树根结点是不保存字符(一般是这样额);
那~简要介绍一下我的这个代码吧:
我是想要若干输入单词,然后输入单词再查一下该单词在不在里面;
我用结构体来存储结点,其中有一个结构体指针类型的数组,(这个数组的每一个元素都是一个指针,结构体类型的,呵呵)
结构体数组只有26个存储26个小写字母只能是小写字母!!!只能是小写字母!!!只能是小写字母!!!
然后一个v:可以表示当前是第几个单词,一个b;
然后我说一下:它并非真正的将 单词的字符 存到节点上,而是利用每一个小写字母与a之间的相对大小,这样默认的就将26个小写字母的位置固定下来了;
我先创建了一个根节点,利用单独的一个函数Creatgen:
每次输入一个字符串都调用一次Insert_DanCi()函数;
Insert_DanCi()函数:先 以 输入第一个字符串 为例 :
一个字符串进来先求他的长度,然后以长度为限制做循环,因为是第一个字符串,在循环过程中只会经历if ,结束;
在if里面,他实现开辟一个空间并用q指向这块空间,会将这个新的还未连接的结点 的结构体数组全变成NULL 然后 v进行相应的变化···················第一个字符保存的时候,v在该节点的值是1;
然后第二个字符串回传递过来:
这时就会从第一个字符查看它的位置是否已经被占据(被占据的话就是相同的前缀),进行else;
进行若干步之后一个树就算 建好了~~~
现在,要记住这棵树已将成型了,是作为样本给我们检测的了;
在搜索的时候,从将传过来的s字符串从 数组的第一个字符 (s[0])开始,如果该位置是空表示没有这个字符直接退出就行了;
若果第一个有的话就依次往下遍历字符串;
就这样吧 ~~~~
对了我在输入的时候出现过一个问题:我一开始有个m值想让m=3,然后循环输入三个字符串,但是只能输入两个!!! 后来我找了 一下资料知道是之前对 gets()函数理解不深导致的;
我在这里再单独的说一下吧:
Gets(a);
Gets(b);
Gets(c);
当输入了a数组的最后输入回车符,这是a数组就输入结束了,回车符保存在缓冲区中;
让b数组接受这个回车符,然后b就会 胎死腹中 只有一个 b[0]=’\0’ ;
这时通过键盘输入字符会全部存到c数组中直到遇到一个回车符或者EOF ,同样,最后的回车符也是放在缓冲区中;
赵先生,请。
#include <stdio.h>
#include <stdlib.h>
#define Max 26
struct Tree{
struct Tree *next[Max];//next[0],next[1]每个数组元素都是一个指针即地址;
int v;
int b;
};
struct Tree* Creatgen(){
int i=0;
struct Tree *p,*root,*q;
root=(struct Tree *)malloc(sizeof(struct Tree));
for(i=0;i<Max;i++){
root->next[i]=NULL;
root->v=0;
}
return root;
}
//这是仅仅的 单纯26个小写字母或者26个大写字母
//并非真是存储而是采用相对位置;
int Insert_DanCi(char s[],struct Tree *root){
int i,j,m,n,id;
struct Tree *p,*q;
p=root;
m=strlen(s);
for(i=0;i<m;i++){
id=s[i]-'a'; //以a为标准 看一看 当前字符应该在指针数组的哪个位置。
//表示该位置还没有被占据,意思就是该字符在当前的这行是首次出现
if(p->next[id]==NULL){
q=(struct Tree *)malloc(sizeof(struct Tree));
q->v=1;//可以看到并没有向节点内存储 字符串中的而字符,而是按照相对位置进行安排,已经默认0—a,1-b,2-c
for(j=0;j<Max;j++){
q->next[j]=NULL;
}
//*接受上一个结点的v值,要记住在for循环中执行if语句表示这是一个单词还没存储结束。
q->v=p->v;
q->v++;
//*
p->next[id]=q;
p=p->next[id];
//p=q;
}
//当参数传来第一条字符串的时候,只会执行if语句;当第二条字符串传来时,才会查看是不是有公共前缀
//当单词的该位置的字符与当前节点存储的字符相同时,就表达为该节点 被占领,然后 p会指向被占领的结点
//为'a'为标准的相对位置,默认的规定了每个字符的位置,这也是判断一个字符有没有占据一个空间位置的 要领。
else{
p=p->next[id];
}
}
p->b='&';
return 0;
}
int Search(char s[],struct Tree *root){
int i=0,j,m,n;
int id=0,con[id];
struct Tree *p,*q;
p=root;
m=strlen(s);
for(i=0;i<m;i++){
id=s[i]-'a';
//表示这棵树的这个位置已经未被占据,即没有单词
if(p->next[id]==NULL){
printf("查无此人\n");
break;
}
//表示这棵树的这个位置已经被占据,也表示和这个单词的这个字母相同
else{
p=p->next[id];
}
}
if(i==m){
printf("欢迎大人,大驾光临!!!\n");
}
return 0;
}
int main(){
int k=0;
int i=0,j=0,m=0,id=0;
char s[123];
int con[1234];
struct Tree *p,*root;
//创建树根;
root=Creatgen();
scanf("%d",&m);
//插入单词,每输入一个单词,调用一次插入单词函数;
//下面这种输入方式:只能输入m-1个字符串,这是为什么呢?
// 因为:输入第一个字符串的时候没有问题,直到第一次输入回车符,将回车符保存在缓冲区并被第二个gets()函数是被,第二个gets()函数就胎死腹中了 只有一个'\0';
//所以,他的数目少一个只有m-1个;;;下面是我改良的;
//for(i=1;i<=m;i++){
// gets(s);
// Insert_DanCi(s,root);
// memset(s,'0',sizeof(s));
//}
//这样虽然 回车符 也会被 字符数组接受 并作为为一个字符串 (胎死腹中的字符串)
//同样我把这种方法用在 寻找 单词上面;
while(gets(s)!=NULL){
Insert_DanCi(s,root);
memset(s,'0',sizeof(s));
}
//还要注意 while(!=EOF)和while(!=NULL)都是输入CTRL + Z结束
//怎么遍历输出树(单词)?
//接下来就是了
//输入一个要查找的单词
//gets(s);
//Search(s,root);
while(gets(s)!=NULL){
Search(s,root);
}
return 0;
}