算法笔记5.Tire树,并查集,堆

Tire树

简介

Tire树是一种用于存储字符串的数据结构,可以高效的实现字符串的插入和查找

思想

从根节点开始,将每一个字符作为一个节点插入,如果后面的字符串有重复字符,则复用之前的节点直到创建新的分支节点,同时会在每个字符串的结尾处做标记,方便查找。

比如我要存储三个字符串“ab”,“ac”,“ba”。

首先插入“ab”,根节点下没有a节点,新建a节点,a节点下没有b节点,新建b节点并标记结束。

然后插入“ac”,根节点下有a节点,复用a节点。a节点下没有c节点,在a下创建c节点并标记s结束。

最后插入“ba”,根节点下没有b节点,新建b节点。b节点下没有a节点,新建a节点标记结束。

代码

int son[N][26];  //son[p][u] 表示节点 p 的第 u 个孩子节点编号
                 //每一个son[p][u]都会有一个son[x]对应
                 //因为节点的孩子节点也是作为一个节点存在数组的一维里面

int cnt[N];      //用于存储每个节点对应的字符串出现的次数
int idx;         //记录当前已使用的节点数。
char str[N];     //要插入的字符串0

//插入字符串
void insert(char str[])
{
    int p = 0;
    for(int i = 0; str[i]; i++)
    {
        int u = str[i] - 'a'; //这个字符在26个字母中的位置
        if(!son[p][u]) son[p][u] = ++idx; //如果不存在这个节点,就新建一个
        p = son[p][u];     //让p指向新的节点
    }
    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; //如果中间有不匹配的说明没有
        p = son[p][u];
    }
    return cnt[p]; //如果最后都匹配,说明有,返回出现次数
}

并查集

简介

并查集就是一个用来快速实现合并和查找根节点的数据结构,一般是一个树形结构是一个集合。合并是将两个集合合并为一个大集合,查找是看一个元素是不是这个集合的。

思想

集合中一个元素不仅存了值,还会存储自己的父节点。合并就是将一个集合的根节点设置为另一个根节点的子节点,然后更新一下父节点。查找就是向上回溯,找父节点的父节点,一直找直到没有父节点为止。

顺便引入一个路径压缩的概念,我们查找一个元素是一直循环找父节点,我们可以查找一个元素的父节点的时候,直接将路径上所有经过的元素的父节点直接更新成根节点,这样再次查找的时候就会直接得到根节点。

代码

int n,m;
int p[N];

int find(int x) //寻找x的祖宗节点,顺便路径压缩
{
    //如果祖宗节点不是它本身,就继续找。顺便压缩
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

简介

堆是一个完全二叉树(左右都很满的那个),其中有一个特点,每个点的值都小于等于左右两个子节点的值

基本操作是插入新元素,查找最小节点,删除最小节点,删除任意一个元素,修改任意一个元素。

思想

用一个一维数组存储,第一个元素存根节点。节点x的左子节点是第2x个元素,节点x的右子节点是第2x+1个元素(就是从上到下,从左到右,先把第一层存完,再从左到右存第二层,再去下一层继续存)

(为了方便操作,会直接用1当索引存根节点)

我们只需要两个基本操作,将节点下移(down)和将节点上移(up)。

当进行down操作的时候,一般是一个节点的值变大了,我们就去看它的子节点,从两个子节点中选一个最小的,与其交换位置。然后继续判断交换完成后的变大的节点的新的子节点,一直交换到不能交换为止。

当进行up操作的时候,一般是一个节点变小了,我们只需要与根节点比较,哪个更小哪个在上面,一直交换到不能交换为止。

接下来再看我们需要的五种操作。

插入新元素:在最下方插入新元素,然后不断上移。

求最小值:就是堆顶部的

删除最小值:用最后一个元素覆盖第一个元素,然后再一直down操作就好。

删除任意一个元素:用最后一个元素覆盖当前元素,然后看是需要down还是up。

修改一个元素:改完以后看down还是up。

代码

int n, m;
int h[N], size;

//建堆
//因为n/2向下取整后是最后一个非叶子节点的位置
//倒着遍历非叶子节点都down一遍
for (int i = n / 2; i; i--) down(i);

void down(int u)
{
	int t = u;
	//如果当前节点大于左节点,令t等于左节点
	if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
	//如果当前节点大于右节点,令t等于右节点
	if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (u != t)
	{
		//交换
		swap(h[u], h[t]);
		//递归
		down(t);
	}
}

void up(int u)
{
	//不用递归,直接循环到结束为止
	while (u/2 && h[u/2] > h[u])
	{
		swap(h[u / 2], h[u]);
		u /= 2;
	}
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值