字典树(链式+数组模拟)--最基础的算法,最详细的注释

知识整理啦啦啦

承前启后,再挂一个入门算法的典范

字典树

啥是字典树,照我看这其实算不上一个算法,就是数据结构里树的第一种表示方式

精髓在用法上,一般用来查询前缀词数量和单词是否存在

一言以蔽之,用以高效查询上述两类问题的一种存储结构

上代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 26     //26个字母


struct tree{
   struct tree *childs[N];    //类似突触用以下一节点
   int count;                        //计数,有多少单词经过此节点,也就是到此节点为止的前缀词数
   int final;                          //标志,表示单词到该字母时结束
};


struct tree *root; //入口,不存储信息,用来找到这棵树 
//另,全局变量有一个好处  不占用有限的函数堆内存



void insert(char *x){  //插入单词,传入一个字符串
   int length;      //记录单词长度
   struct tree *p,*q;  //用以操作树的节点的指针
   length=strlen(x);  //获取单词长度
   p=root;  //进入树中
   for(int i=0;i<length;i++){  //从头遍历单词
      if(p->childs[x[i]-'a']!=null){  //也从树的头处开始询问当前字母是否存在
         p=p->childs[x[i]-'a'];  //存在,进入该字母所在的节点
      }
      else{
          q=new tree;  //不存在,先建立这样一个节点
          p->childs[x[i]-'a']=q;  //将指针指过去
          p=q;  //同样进入这个节点
      }
      p->count++;//该单词经过了这个节点,故计数加一
   }
   p->final=1;  //遍历完了,指向的是最后一个字母,设置结束标志
}



int findprefix(char *x){  //传入前缀,查询前缀词数
   int length;  //长度
   struct tree *p;  //用以操作树的节点的指针
   length=strlen(x);  //获取长度
   if(!length)return 0;  //空串,返回0个
   p=root;  //进入树中
   for(int i=0;i<length;i++){  //遍历前缀
      if(p->childs[x[i]-'a']!=null){  //也从树的头处开始询问当前字母是否存在
         p=p->childs[x[i]-'a'];  //存在,进入该字母所在的节点
      }else{  
      return 0;  //不存在,就没这个前缀,返回0个
     }
     return p->count; //遍历完了,有count个单词经过这个节点就说明有这么多个前缀词
}



int findword(char *x){  //传入单词,查询前缀词数
   int length;  //长度
   struct tree *p;  //用以操作树的节点的指针
   length=strlen(x);  //获取长度
   if(!length)return 0;  //空串,返回0表示不存在
   p=root;  //进入树中
   for(int i=0;i<length;i++){  //遍历单词
   if(p->childs[x[i]-'a']!=null){  //也从树的头处开始询问当前字母是否存在
         p=p->childs[x[i]-'a'];  //存在,进入该字母所在的节点
      }else{  
      return 0;  //不存在,就没这个单词,返回0表示不存在
     }
     if(p->final==1) return 1:  //遍历完了,若最后一个字母是某词的结尾,则表示有该词,返回存在
     else  return 0;  //否则,表示这个单词是某个前缀,返回不存在
}



int main(){
   root= new tree;
  /*
  输入单词或查询
  */
   return 0;
}

get到了这个算法,但

回头一想查,询单词是一件很简单的事对吧

那为啥要有这个算法呢

因为快,有多快呢,举个栗子
你的词库里有10亿个单词(嗯 尽是些长单词)

如果是用朴素方法,需要多少开销呢

查询单词还好些,用二分也没几次,但你要先排序(时间复杂度天文数字)

要是查询前缀,一个个匹配(大天文数字)

但对字典树来说,只要遍历一个单词就好了,和词库容量无关,多少词都这个速度,看起来好像挺快的

已经这么快了,只能换个角度去优化了,在内存上有好多悬空指针啊,浪费!

于是

数组模拟字典树(降速减容)

#include<stdio.h>
#include<string.h>
#define N 900000  
int tree[N][26],count[N]; //看成N个连续的地址空间 每个空间有26个通道和一个计数 
int pc;  //模拟连续内存的首地址,是几无所谓啦,默认0 
void insert(char x*){   //插入单词 
	int length;  
	length=strlen(x);  //获取单词长度 
	int p=0;  //类似指针,数值就是地址了 
	for(int i=0;i<length;i++){  //遍历单词 
		int j=x[i]-'a';   //字母转义对应26个下标 
		if(tree[p][j]==0)  //0地址的空间作为root了,判断该字母是否存在 
		tree[p][j]=++pc;   //不存在,选取下一个空的空间连通,作为该字母的节点 
		//说明下参数 p表示当前节点的地址,j表示字母的通道,数值就是通道那头节点的地址了 
		p=tree[p][j];   //进入该字母的节点 
		count[p]++;   //单词经过了这个节点,计数加一 
	}
}
int find(char *x){  // 传入前缀 
	int length;   
	length=strlen(x);  //获取长度 
	p=0;    //类似指针,数值就是地址了 
	for(int i=0;i<length;i++){  //遍历前缀 
		int j=x[i]-'a';    //字母转义对应26个下标
		if(tree[p][j]==0) return 0;  //不存在,结束没这个前缀 
		p=tree[p][j];   //进入该字母节点 
	} 
	return count[p]; //遍历结束,返回前缀词数 
} 

ps:因为第一个链式结构用的是动态生成的方法,其实两个用的内存差不多

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值