数据结构
文章平均质量分 85
总结了一些常见的数据结构
爱寂寞的时光
这个作者很懒,什么都没留下…
展开
-
可并堆(左高树、左偏树)
可并堆(左高树、左偏树)左偏树相对于二叉堆,其插入,弹出,合并的时间复杂度都是对数级别的。高度优先左偏树(HBLT)考虑一颗二叉树,将其叶子节点的子节点补全,那么原来有的节点叫做内部节点,新增加的节点叫做外部节点。令s(x)s(x)s(x),等于节点xxx到任意外部节点的最短路径的长度。外部节点的s(x)s(x)s(x)的值为000,叶子节点的值为111。其递归定义为:s(x)=min{s(L),s(R)}+1s(x) = \min\{s(L),s(R)\} + 1s(x)=min{s(L),s原创 2021-07-28 15:05:06 · 341 阅读 · 0 评论 -
线段树进阶
zkw线段树zkw线段树由清华大学张昆玮(zkw)发明,相较于普通线段树,其优点有:非递归,时间常数小使用二进制思想,代码短,方便书写建树我们强制要求叶子节点(即区间大小为1的节点)排列在最后一行,并且还是一颗满二叉树(多余的节点不用)。这样我们就可以写出建树的代码,此时只更新了叶子节点的信息,我们没有更新非叶子节点的信息。void build(int n){ // 寻找最后一行的大小,也是非叶子节点的大小 for (M = 1; M < n + 2; M <原创 2021-07-07 19:56:07 · 206 阅读 · 1 评论 -
伸展树(Splay)
伸展树(Splay)Splay 是一种二叉查找树,它通过不断将某个节点旋转到根节点,使得整棵树仍然满足二叉查找树的性质,并且保持平衡而不至于退化为链。它由 Daniel Sleator 和 Robert Tarjan 发明。旋转旋转(Rotate)的最终目的是将该节点的深度−1-1−1并且保证二叉搜索树的结构不改变。根据节点xxx是父节点的左子节点或者是右子节点分为右旋和左旋转。我们以右旋为例,我们发现,如果我们想让节点XXX的深度−1-1−1,我们必须让XXX做ZZZ的子节点,至于是左右子节点,原创 2021-07-06 13:50:36 · 265 阅读 · 1 评论 -
树查询算法
树上差分思想是将树看成是相交线段进行差分。分为点差分和边差分。通常需要结合LCA来实现。其实更推荐树链剖分,树链剖分可以很好的把LCA和线性结构组合在一起。点差分以统计一条路径上经过的点的次数为例子。我们定义节点xxx的差分变量:diffx=valx−∑s∈sonxvals\text{diff}_x = \text{val}_x - \sum_{s \in \text{son}_x} \text{val}_sdiffx=valx−s∈sonx∑vals我们有路径δ(s,t)\del原创 2021-07-05 15:08:51 · 455 阅读 · 0 评论 -
Treap(树堆)
Treap(树堆)Treap(树堆)是一种平衡二叉搜索树实现。分为带旋转的Treap和无旋转的Treap。其中,无旋转的Treap相比于AVL来说,其实现简单,方便快捷,是OI竞赛中的不二之选,但是因为其依靠随机化的因素,其平衡是期望平衡的,最坏情况退化为链表,但概率不大。其中无旋Treap(以下Treap均指无旋Treap),分为两个重要的操作,分别为Split(分割)、merge(合并)。本文以为P3369例题,讲解其实现。节点结构体其节点和二叉树基本相同,其中prior成员需要特别注意,这个成原创 2021-06-02 23:01:29 · 385 阅读 · 2 评论 -
可持久化线段树(主席树)
可持久化线段树简介可持久化线段树,又称主席树。对于普通的线段树,想要支持回退操作(Undo)十分困难。可持久化线段树应时而生,可持久化线段树可以支持在不同版本直接进行穿梭,修改和查询。基本原理以单点修改为例,对于一次单点修改创建一个新的版本,朴素的思路是将这个线段树从头到尾拷贝一份,但是拷贝操作不管是空间还是时间上的复杂度都是极高的。我们观察一下单点修改操作的修改过程。我们把它变成圆形节点的形式:其中,以橘色节点组成的路径即为修改路径,我们发现,只有修改路径上的节点的节点信息改变了,而其他节原创 2021-05-17 17:27:23 · 842 阅读 · 0 评论 -
珂朵莉树
珂朵莉树名称简介老司机树,ODT(Old Driver Tree),又名珂朵莉树(Chtholly Tree)。起源自 CF896C。核心思想分块,把块按照顺序储存在一起。把值相同的区间合并成一个结点保存在 set 里面。复杂度骗分。只要是有区间赋值操作的数据结构题都可以用来骗分。在数据随机的情况下一般效率较高,但在不保证数据随机的场合下,会被精心构造的特殊数据卡到超时。如果要保证复杂度正确,必须保证数据随机。如果数据不够随机,区间比较紧密,那么将会出现许多碎片化的区块,就像磁盘一样。对于 a原创 2021-03-29 22:48:01 · 340 阅读 · 0 评论 -
种类并查集
种类并查集在一般的并查集当中,普通并查集只能维护一般的等价类关系,如果存在多个等价类关系,就需要用到种类并查集了。原理集合中的元素直间存在两种关系,假设AAA与BBB的关系是关系1,BBB与CCC的关系是关系222,那么AAA和CCC一定存在关系,这种情况才能用种类并查集(即关系传递)。例如:朋友与敌人关系,朋友的朋友是朋友,朋友的敌人是敌人,敌人的朋友是敌人,敌人的敌人是朋友。这种相互传递的关系才能用种类并查集,如果是单链的关系,是不能用种类并查集维护的。我们创建三倍空间的大小的并查集,其中另外原创 2021-03-29 19:14:07 · 121 阅读 · 0 评论 -
树链剖分
树链剖分对于一颗树来说,维护树上任意一段路径信息比较困难,但是我们可以把树拆成多个链,这种处理和维护树上路径信息的方法叫树链剖分(树剖、链剖)。树链剖分有多种形式,比如重链剖分、长链剖分和用于 Link/cut Tree 的剖分(有时被称作“实链剖分”),大多数情况下(没有特别说明时),“树链剖分”都指“重链剖分”。重链剖分可以将树上的任意一条路径划分成不超过O(logn)O(\log n)O(logn)条连续的链,每条链上的点深度互不相同(即是自底向上的一条链,链上所有点的 LCA 为链的一个端点原创 2021-03-27 20:41:46 · 280 阅读 · 0 评论 -
AVL平衡树
AVL平衡树对于一般的二叉搜索树来说,插入元素很容易导致树的高度向一个方向偏斜,在最坏情况下二叉搜索树退化成链表,其时间复杂度为O(n)O(n)O(n)。此时,我们就需要做一个高度自平衡的二叉搜索树——AVL平衡树。节点信息和普通的树节点相比,AVL平衡树的节点信息多了一个高度信息,记录了这颗子树的高度,并规定空树的高度为000,单一节点的高度为111,以此类推。struct Node{ int val; Node *left; Node *right; int h原创 2021-03-13 15:56:56 · 118 阅读 · 0 评论 -
数据结构——树状数组
树状数组树状数组是一个查询和修改复杂度都为log(n)\log(n)log(n)的数据结构。主要用于数组的单点修改和区间求和。另外一个拥有类似功能的是线段树。树状数组类似于线段树的简化版本,线段树能做的树状数组基本都能做。树状数组的结构树状数组的储存通常用一个数组来实现,数组的下标就是节点的编号。令T[i]T[i]T[i]为树状数组,而C[i]C[i]C[i]为原数组。T[i]T[i]T[i]的节点值iii,写成二进制形式,例如树状数组中的节点值iii为1101011002110101100_原创 2021-03-09 12:54:56 · 195 阅读 · 0 评论 -
01Trie树
子集查找树LeetCode 1178 猜字谜前言如果你看到了这篇题解,那么你应该对二进制状态压缩并枚举子集+哈希表和Tire树的做法有了一定的认识,那么我们能不能把这两种方法结合起来呢,答案是可以的,使用一种神奇的数据结构——子集查找树。另外,本题又要求加上必须包含首字母的这个条件,所以我们需要魔改一下。子集查找树子集查找树类似与Tire树,只不过Tire树处理的是字符串序列,而子集查找树处理的是二进制序列。类比与Tire树,子集查找树中每个节点有两个子节点,分别是0和1,代表了二进制序列中的下一原创 2021-02-26 12:10:21 · 681 阅读 · 0 评论 -
对顶堆——中位数神器
对顶堆——中位数神器对顶堆,即一个大根堆和一个小根堆组合而成的一个数据结构,可以很方便的维护可变区间中位数。平衡值如果固定序列的大小为nnn,那么平衡值指堆的最大大小。如果nnn为偶数,大根堆的平衡值leftNumleftNumleftNum等于小根堆的平衡值rightNumrightNumrightNum等于n2\frac{n}{2}2n。如果nnn为奇数,则leftNum=n/2leftNum = n/2leftNum=n/2(整除),rightNum=n−leftNumrightNum =原创 2021-02-25 23:54:03 · 568 阅读 · 0 评论 -
树的计数问题-Prufer 序列
树的计数问题-Prufer 序列问题引入P2290设一颗树有nnn个节点,分别是v1,v2,…,vnv_{1},v_{2},\ldots,v_{n}v1,v2,…,vn,其度分别为d1,d2,…,dnd_{1},d_{2},\ldots,d_{n}d1,d2,…,dn,问构成这样的无根树总共有多少种?Prufer 序列很明显,在一颗树上用排列组合定理是相当困难的,我们能不能把一颗无根树转换成对应的唯一一个序列,即让一个序列与一个无根树一一对应,构成双射,然后就可以对线性的序列使用排列组原创 2021-02-15 18:36:44 · 572 阅读 · 0 评论 -
一维/二维线性前缀和&差分
一维/二维线性前缀和&差分线性前缀和差分是解决数据结构——区间的最基本的工具,能够高效的解决区间修改,单点查询。除了线性前缀和&差分,还有树上前缀和&差分。一维线性前缀和&差分没啥好说的,不说了。二维线性前缀和&差分设原数组为a[i][j]a[i][j]a[i][j],差分数组为b[i][j]b[i][j]b[i][j],那么根据一维差分的思想:原数组是差分数组的前缀和,我们可以推出:a[i][j]=b[1][1]+b[1][2]+…+b[1][j]+b[原创 2021-02-18 13:15:40 · 95 阅读 · 0 评论 -
稀疏表(ST表)
稀疏表(ST表)此文介绍一种数据结构——ST表(Sparse Table),以及如何使用这个数据结构解决可重复贡献问题。可重复贡献问题可重复贡献问题指的是,对于一种运算optoptopt,满足opt(x,x)=xopt(x,x) = xopt(x,x)=x,并且optoptopt可以参与区间运算,满足∀l≤k≤r,opt [l,r]=opt(opt [l,k],opt [k,r])\forall l \leq k \leq r,opt \ [l,r] = opt(opt原创 2021-02-08 23:28:55 · 378 阅读 · 0 评论 -
单调栈、单调队列
单调栈、单调队列单调栈、单调队列是在栈和队列的基础上加上单调结构的数据结构。如果一个元素入栈或入队,他会检查之前的元素,如果之前的元素不可能是答案的解,那么就弹出元素,使得当前元素入栈或入队。例题LeetCode 239 滑动窗口最大值此题是单调队列,每次遇到一个元素,一直从队尾弹出,直到队尾元素大于该元素为止。还需要注意检查队头元素是否在区间内。LeetCode 84 柱状图中最大的矩形此题为单调栈,首先的思路是先计算每一个柱子能到达的左边的最远距离和右边的最远距离,然后在遍历一次数组即可。我原创 2021-02-08 21:42:31 · 77 阅读 · 0 评论 -
Morris迭代算法+迭代器版本
Morris迭代算法+迭代器版本Morris迭代算法的核心思想是取root的前驱节点,然后判断前驱节点的后继标记是不是root,如果是,那么说明前面的前驱已经全部输出完毕,那么就清空前继节点的后继标记,然后按序遍历,如果不是,那么就继续按续遍历。如果root没有前继节点,那么就输出root节点,向后遍历即可。中序遍历我们一般用Morris迭代算法都是中序遍历,大多数用于二叉搜索树非常好用。算法步骤:如果当前节点的左子节点为空时,输出当前节点,并将当前节点置为该节点的右子节点;如果当前节点的左子转载 2021-02-05 15:56:11 · 198 阅读 · 0 评论 -
线段树详解
线段树详解问题引入给定一段数组[d1,d2,...,dn]\left [ d_{1},d_{2},...,d_{n} \right ][d1,d2,...,dn],定义下列操作:单个元素修改:将dnd_{n}dn加上或减去一个值区间元素修改:给定左闭右开区间[a,b)\left [ a,b \right )[a,b),将下标在区间[a,b)\left [ a,b \right )[a,b)中的元素分别加上或减去一个相同的值单个元素查询:查询dnd_{n}dn的值区间元素查询:给定左闭原创 2020-09-16 10:41:10 · 207 阅读 · 0 评论