数据结构:字典树Trie

文章介绍了Trie树作为一种高效的数据结构,用于存储和查找字符串集合。通过图解展示了Trie树如何存储字符串,并解释了如何通过Trie树进行查找操作。此外,还探讨了如何利用Trie树优化最大异或查询问题,提供了一种使用Trie树解决此类问题的代码实现。
摘要由CSDN通过智能技术生成

作用

用来高效快速存储和查找字符串集合的一种数据结构

存储图解

假设有若干个字符串在这里插入图片描述

先创建了一个根结点root,代指字符串集合的开头,对第一个字符串abcdef存储就如上图所示

在这里插入图片描述

当存储abdef时,一直走到b都和abcdef相同,于是在b点另开一个分支存储之后的def

当存储aced时,一直走到a都和abcdef相同,于是在a点另开一个分支存储之后的ced

以此类推

我们就得到能够以树的形式存储一个字符串集合

在这里插入图片描述

并且在每个字符串的结尾的字母都标记一下,表示以该字母结尾的节点是存在一个字符串的

如果有类似abc这样的字符串,也要在c点标记一下

查找图解

如果要查找一个不存在的字符串,比如abcf

在这里插入图片描述

沿着树枝一直走,走到c之后发现没有f对应的路径,那么就说明该字符串不存在

或者查找一个字符串,该字符串在存储里的字符串不存在,但属于某个存储的字符串的前缀

比如abcdef中的abc作为要查找的字符串

沿着树枝走,发现c点没有标记,即使有对应对应路径也不能说明该字符串存在

代码实现

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>

using namespace std;

const int N = 100010;

int son[N][26];//代表每个节点可以有26个子节点
int cnt[N];//以当前这个点结尾的字符串有多少个
int idx;//操作次数,当前用到了哪个下标
//下标是0的点,既是根节点,也是空节点
char str[N];

void insert(char str[])//插入新字符串
{
	int p = 0;//根节点
	for (int i = 0;str[i];i++)
	{
		int u = str[i] - 'a';//存储编号,字母a-'a'就变成了0,b就变成了1
		//如果说该节点之后没有这个字母,即son[p][u] == 0
		if (!son[p][u]) son[p][u] = ++idx;//把这个节点加进去
		p = son[p][u];
	}

	cnt[p]++;//表示以该点结尾的数量又多了一个	
}

int query(char str[])//查询字符串
{
	int p = 0;
	for (int i = 0;str[i];i++)
	{
		int u = str[i] - 'a';
		if (!son[p][u]) return 0;//如果不存在直接返回0
		p = son[p][u] ;
	}

	return cnt[p];//返回以该节点结尾的单词数量
}
int main()
{
	int n;
	scanf("%d", &n);
	while (n--)
	{
		char op[2];//操作类型+空格
		scanf("%s%s", op,str);

		if (op[0] == 'I') insert(str);//插入操作
		else printf("%d\n", query(str));//查询操作
	}

	return 0;
}

习题

在这里插入图片描述

暴力做法

利用两重for循环来寻找最大异或

	//暴力法
	for (int i = 0;i < n;++i)
	{
		for (int j = 0; j < i;++j)
			res = max(res, (arr[i] ^ arr[j]));
	}

能被看作是trie树的原因

因为运算方式是异或,而异或看的是二进制,如果两个数的二进制某一位相同则为0,不同则为1

因此可以用31个长度的数组存储两个数的每一位

且对二进制从左往右看时,假设一个数为0,可以用trie树查询最高位为1的数,再找下一位为1的数,以此类推,便可以快速找出异或后的最大数

在这里插入图片描述

因此,对某个数查询能够在存储里的数寻找最大异或时,可以从最高位依次查找每一位相异的数

实现代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;
const int M = N * 31;

int arr[N];
int son[M][2];
int idx;

void insert(int x)
{
	int p = 0;//尾节点
	for (int i = 30;i >= 0;--i)
	{
		int u = x >> i & 1;//得到该位
		if (!son[p][u]) son[p][u] = ++idx;
		p = son[p][u];
	}
}

int query(int x)
{
	int p = 0;
	int res = 0;
	for (int i = 30;i >= 0;i--)
	{
		int u = x >> i & 1;
		//如果该位相异的数存在
		if (son[p][!u])
		{
			p = son[p][!u];
			res = res * 2 + !u;//相当于把所有的位数进一再加上u
		}
		else
		{
			p = son[p][u];
			res = res * 2 + u;
		}
	}

	return res;
}
int main()
{
	int n;
	scanf("%d", &n);

	for (int i = 0;i < n;++i) scanf("%d", &arr[i]);

	int res = 0; 

	for (int i = 0;i < n;++i)
	{
		insert(arr[i]);
		//先插入在查询,防止第一次查询不到的情况
		int t = query(arr[i]);

		res = max(res, (arr[i] ^ t));
	}
	printf("%d", res);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涅槃豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值