字典树Trim详解+相关例题---龙之介算法基础课

字典树,顾名思义,是关于“字典”的一棵树。 即:它是对于字典的一种存储方式(所以是一种数据结构而不是算法)。 这个词典中的每个“单词”就是从根节点出发一直到某一个目标节点的路径,路径中每条边的字母连起来就是一个单词。
在这里插入图片描述
实际上不一定是字典,也可以是二进制哦
在这里插入图片描述

我们要理解一个概念,字典树是一种数据结构,类似字典的查找很快的结构。故它拥有insert插入和qurey查找功能。
需要注意的是,有些值可能会在到达树的末梢之前就可以返回了[也就是说不一定是一条边一个字母],例如:
在这里插入图片描述
OK,理顺了概念,直到他是What,下面我来考虑如何代码HOW实现
我们知道,字典树具有和字典一样的插入和查询的功能。

我们先来看插入:
每次都从根结点开始搜索,然后依次遍历你需要插入单词的字母,如果该字母没有在树中,则添加此字母结点到树根。
直到所有字母遍历完毕,则标记最后一个结点

再来看看查询:
查询和插入其实完全一致,只是少了插入这个环节。每次都从根结点开始搜索,然后依次遍历你需要插入单词的字母,如果该字母没有在树中,则说明找不到此结点。
直到所有字母遍历完毕,说明找到了

总结就是:每次从根结点开始遍历,遍历过程中判断是否存在此字母结点,直到整个单词遍历完毕

接下来是伪代码模板:

const int M = 100000000000000//反正应该很大具体怎么算你。。。
const int N = 26;//如果是字母就是26,二进制就是2
int trim[M][N],idx;//trim是字典树,idx是指针索引
int cnt[M];
//插入
insert(T[] data)
{
	int p=0;
	//遍历整个单词
	for(int i=0;i<data.size;i++)
	{
		//如果没有此结点就新建结点
		if(trim[p][data[i]]==0)trim[p][data[i]]=++idx;
		p=trim[p][data[i]];//继续到下一结点
	}	
	cnt[p]++;//标记最后一个结点
}
//查找
query(T[] data)
{
	int p=0;
	//遍历整个单词
	for(int i=0;i<data.size;i++)
	{
		//如果没有此结点就没有这个单词
		if(trim[p][data[i]]==0)return false;
		p=trim[p][data[i]];//继续到下一结点
	}	
	return true;//此处也能返回cnt,表示此单词出现次数
}

模板仅供参考,理解思路才是真理。
好了,现在知道What和How,那么Why?,那还不是为了学算法以后找工作,不然狗都不学系列。顺带一提,字典树能写的题,一般用普通的哈希表其实也能写,STL这么香为什么不用。
可,现在来上leecode题目。因为我开头说了字典树可以解决诸如单词或者二进制的存储,所以,我就举一个单词类的题和一个二进制的题。

前K个高频单词
在这里插入图片描述
这题是非常经典的字母计数类型的题了
这就用到了cnt数组,刚好用来计数。
顺带一提 这题其实用map代替自己写的trim也是完全可以的。

bool cmp(const pair<string,int>& s1, const pair<string,int>& s2)
{
    if(s1.second==s2.second)
    return s1.first<s2.first;
    else
    return s1.second > s2.second;
}
class Solution {

public:
    int son[100010][26],idx;
    int cnt[100010];
    void insert(string str)
    {
        int p=0;
        for(int i=0;i<str.size();i++)
        {
            int u=str[i]-'a';
            if(!son[p][u])son[p][u]=++idx;//如果没有就建根
            p=son[p][u];
        }
        cnt[p]++;
    }
    int quray(string str)
    {
        int p=0;
        for(int i=0;i<str.size();i++)
        {
            int u=str[i]-'a';
            if(!son[p][u])return -1;//如果没有找到就返回
            p=son[p][u];
        }
        return cnt[p];
    }


    vector<string> topKFrequent(vector<string>& words, int k) {
        set<string> s;
        for(int i=0;i<words.size();i++)
        {
            insert(words[i]);
            s.insert(words[i]);
        }
        vector<pair<string,int>> res;
        for(set<string>::iterator it=s.begin() ;it!=s.end();it++)
        {
            res.push_back({(*it),quray((*it))});
        }
        sort(res.begin(),res.end(),cmp);
        vector<string> ans;
        for(int i=0;i<k;i++)ans.push_back(res[i].first);
        return ans;
    }
};

那么下一题
在这里插入图片描述
这题是比较经典的字典树的题目了。
前提是你对二进制的基本功掌握不错。
这里插入操作是将x作为二进制插入进字典树
查找操作是查找和x异或最大的值【也就是答案res】

class Solution {
public:
    
    int son[300000][2],idx;
    void insert(int x)
    {
        int p=0;
        for(int i=30;~i;i--)
        {
            int &s=son[p][x>>i&1];
            if(!s)s=++idx;
            p=s;
        }
    }
    int query(int x)
    {
        int res=0,p=0;
        for(int i=30;~i;i--)
        {
            int s=x>>i&1;
            if(son[p][!s])
            {
                res+= 1<<i;
                p=son[p][!s];
            }
            else p=son[p][s];
        }
        return res;
    }
    
    int findMaximumXOR(vector<int>& nums) {
        for(int i=0;i<nums.size();i++)insert(nums[i]);
        int res=0;
        for(int i=0;i<nums.size();i++)res=max(res,query(nums[i]));
        return res;
    }
};

最后

算法学习的路上是非常艰辛无聊孤独痛苦无助自闭的过程,但是如果你坚持走下去了,那么当你拿到年薪百万的时候你会感谢那个曾经熬过七重孤独的自己
----龙之介

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值