字典树

字典树(学习笔记)

这两天开始弄字典树的问题,感觉有必要整理一下。写一下自己的模板,也整理一下思路。不仅仅给后来人看,更可以加深一下自己对这个问题的理解。

字典树,又称单词查找树,Trie树,是一种树形结构,哈希表的一个变种。用于统计,排序和保存大量的字符串(也可以保存其他的)。优点就是利用公共的前缀来节约存储空间。在这举个简单的例子:比如说我们想储存3个单词,sky、skyline、skymoon。如果只是单纯的按照以前的字符数组存储的思路来存储的话,那么我们需要定义三个字符串数组。但是如果我们用字典树的话,只需要定义一个树就可以了。在这里我们就可以看到字典树的优势了。

上面只是写的字典树的定义,对于一个新的知识点,我们学习的最终目的是学以致用。下面我们来讲述字典树的应用。在百度百科上对字典树的操作写的是查找,插入,删除,但是我觉得还应该有一个建立。建立上以上三个操作的基础。没有建立其他根本就无从谈起。

好下面咱们开始一步一步的学习字典树的应用。在这里我就以HDU1251题统计难题为模板写的。但是无论是哪个题目,其实他的思想是一致的,一理通百理通。

1.建立:

这个是关键。这个地方用到了链表,如果不了解链表的同学还是请先百度一下链表,然后做一下链表的题目吧。无论哈希还是字典树,链表都是基础。好了,不废话了。注意看我代码中的注释。

定义:

1struct node
2{
3    int count ;
4    struct node *next[26];//定义26个分节点
5};

建立节点:

01struct node *build()//建立节点
02{
03    struct node *p;
04    p=(struct node *)malloc(sizeof(struct node));
05    for(int i=0;i<26;i++)
06    {
07        p->next[i]=NULL;//令其指向0;
08    }
09        p->count=1;/*如果出现这个站点,那么它的次数肯定是大于等于1的。
10        但是我们刚开始建立不必考虑后续问题*/
11    return p;//返回新建立的节点
12}

补充说明一点,代码中的next[26]可以根据自己的需要修改,比如说我只是储存一个数字,那么我只要定义数组到10即可。其实这个代表的是下个节点的个数。

为什么要这么做呢?我们用字典树的目的是什么?我们要用一个树储存下一个单词,或者一个很大的数字。每个节点只放一个数字或者字母。这个节点都用链表把它链接起来。建立结束之后我们可以把它看作是一个树。

2.储存(插入)

好,上一步我们已经建立了一个简单的构建,但是我们并没有把这些节点链接起来,或者说我们并没有给他们安排好辈分问题。(谁是爹,谁是儿子的问题)

那么下面我们就开始明确他们之间的关系:

01void save(char *s)//储存字母
02{
03    int len=strlen (s);
04    if(len==0)return ;
05    struct node *p;
06    p=root;
07    for(int i=0;i<len;i++)
08        if(p->next[s[i]-'a']!=NULL)
09            /*如果这个节点之前就已经存在呃,我们只需要把统计次数加上1.*/
10        {
11            p=p->next[s[i]-'a'];
12            p->count=p->count+1;
13        }
14        else//如果不存在的话,我们就建立一个新的节点
15        {
16            p->next[s[i]-'a']=build();
17            p=p->next[s[i]-'a'];
18        }
19}

如果你真的细心的看的话,其实我们开始的时候并不是建立一个完整的树(也就说我根本不知道他到底有多少层,到底有多少分支)。我上一步只是建立的节点,现在就是动态的建立节点。如果之前这个节点存在的话,我就不需要再建立新的节点了,如果没有的话,我才需要建立新的节点,新的分支。

3.查询(查找)

到现在为止,我们已经建立了一个字典树,我们也明确了他们的从属问题(不会因为让谁当爹而大打出手)。下面我们就需要完成对字典树的查找或者统计了。

01int seach (char *s)//这个函数来查询数据
02{
03    struct node *p;
04    int len=strlen(s);
05    if(len==0)return 0;
06    p=root;//此处小心
07    for(int i=0;i<len ;i++)
08    {
09        if(p->next[s[i]-'a']!=NULL)//说明还有下个节点,继续查询
10            p=p->next[s[i]-'a'];
11        else
12            return 0;
13        /*如果没有指向下个节点,说明这个要查询的字符串根本不存在,直接返回0*/
14    }
15    return p->count;
16}

仔细看的话,你会发现我们判断是否已经到达底部的标准是他的下一个指向是否为空。

4.删除(点或者节点)

对于这个功能,我表示很尴尬,因为我之前没有用到过,也没有人教过我,不过百度的时候他们说用到的情况很少,所以““所以我就没怎么弄。好吧,如果我以后学会了,肯定会写上的,如果你会的话,还请你不吝指教。小弟万分感谢。

我会的也基本上都说完了,下面就是具体的应用了。

小试牛刀:

上面那个题我之前就写了解题报告,下面给出链接

HDU 1251 统计难题(点击打开)

NYOJ 163题 Phone List(点击打开)

NYOJ 290题 动物统计加强版(点击打开)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值