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;
}
}