数据结构学习笔记(时间复杂度分析)
目录
数据结构学习笔记
数据结构
栈(stack)
- 栈是一种后进先出的线性数据结构
- 支持\(push(x)\)将x插入栈顶,\(pop(x)\)将栈顶元素出栈,\(gettop(x)\)取出栈顶
- 时间复杂度都为\(O(1)\)
- 常用的应用有 计算表达式,单调栈,维护操作方便撤销
队列(queue)
- 队列是一种先进先出的线性数据结构
- 支持\(push(x)\)将x插入队列尾,\(pop(x)\)将队列首出队列,\(getfrong(x)\)取出队列首
- 时间复杂度都为\(O(1)\)
- 常用的应用有 bfs,spfa,优先队列,双端队列,单调队列,循环队列
链表
- 链表是一种插入方便而难以查询某一特定的值的线性数据结构,与数组结构互补
- 其中\(insert(x)\)在特定位置插入一个值,\(erase(x)\)在特定位置删除一个值,时间复杂度为\(O(1)\)
- \(find(x)\)查找一个值x,复杂度为\(O(N)\)
- 常用的应用有 邻接表
二叉堆(heap)
- 二叉堆是一种有堆性质的完全二叉树,故支持查找最值,深度为\(\log{N}\)
- 其中\(up(x)\)把x节点向上调整,\(down(x)\)把节点x向下调整,复杂度与深度有关,故均为\(O(\log{N})\)
- 常用的应用有 维护动态最值,优化dijkstra、spfa
哈夫曼树
Trie、01Trie
- Trie是用于字符串快速检索的多叉树,每个节点维护一个字符,同时一般还要维护每个节点是否为字符串结尾
- 支持\(insert(str)\)将一个字符串插入Trie,\(search(str)\)查询一个串是否存在,因为是每一位去储存或遍历,时间复杂度均为\(O(|str|)\)
- 空间复杂度为\(O(NC)\)C为字符集大小
- 其中若我们将每个数变为二进制串存在Trie中,可以得到一个01Trie
- 常用的应用有 字符串检索,前缀检索,前缀统计,字符串DP,计算异或最大值
可并堆(左偏树)
- 左偏树是一种支持合并操作的具有堆性质的二叉树
- 外节点指一个左儿子为空或右儿子为空的节点
- 定义每个节点的dist为后代中离他最近的外节点到其的距离,空节点dist为-1
- 其中我们让一个每一个节点左儿子的dist大于右儿子,使结构左偏,故一个节点的dist为其右儿子dist+1
- \(h=Merge(h1,h2)\)即合并操作,先比较两个堆的最值,小的堆顶为新的顶,再递归子树与堆顶大的堆合并,
- 考虑到每次回减少一半节点,合并操作复杂度为\(O(\log{N})\)
- 在建树的时候可以先1-1合并,再2-2合并,再4-4合并....复杂度为\(O(N)\)
- 常见应用有 维护可合并集合的最值
并查集
- 维护集合的利器,但不支持分裂(按秩合并后利用栈维护操作和支持撤销)
- 每个节点维护其集合中的一个元素,呈树形结构,故会有一个代表元素
- 路径压缩即在找代表元素后,将此次查找到的节点都指向代表元素,\(find(x)\)找到集合代表元素,复杂度均摊为\(O(\log{N})\)
- 另一个常用的优化为按秩合并(启发式合并),将秩小的集合合并到秩大的集合,\(find(x)\)复杂度均摊也为\(O(\log{N})\)
- 如果两个一起用则\(find(x)\)为\(O(\alpha{N})\),其中\(\alpha\)为反阿克曼函数
- 若树的结构需考虑,则不适用路径压缩
- 带权并查集指每个节点维护一个权值,在压缩路径时压缩这个值,维护一些额外的信息
- 扩展域并查集指维护每个节点的对立集合,如储存敌人,则敌人的敌人为朋友
- 常用的应用有 带权并查集,扩展域并查集,维护静态连通性,输出方案时记录fa
st表
树状数组
- 基于二进制拆分的树形结构,\(c[x]\)表示a序列\(\sum_{i=x-lowbit(x)+1}^{x}{a[i]}\)
- 通常维护一个前缀信息
- 因为是二进制拆分,求前缀和和单点修改复杂度为\(O(\log{N})\)
- 考虑其树结构,x的父节点为\(x+lowbit(x)\),故可以在建树的时候将\(c[x]\)加到\(c[x+lowbit(x)]\)上,实现\(O(N)\)建树
- 常用应用有 求逆序对,求区间和、前缀、后缀,二维数据结构,离线做法
线段树
- 线段树是一种基于序列分治的leay tree结构
- 常用的应用有 区间操作(+,-,xor,*,赋值,取相反数,取min,取max),维护区间(最大字段和,max,sum,min,乘积和,gcd,历史最大值)、线段树合并分裂、线段树分治、动态开点、权值线段树、二维线段树、主席树、动态开点、扫描线、树剖
块状链表
平衡树(splay,treap,非旋treap,笛卡尔树)
- 二叉树BST性质指的是一个节点的权值大于左子节点的权值,小于右子节点的权值
- 二叉搜索树即为有BST性质的二叉树,方便支持查询sub,pre,val,rank和insert,erase操作
- 但由于特殊数据会使其复杂度退化如链,故要用一些平衡操作使其期望或均摊复杂度更优
- 常见操作有旋转、重构、特殊性质如堆性质等
- 基于\(rotate\)的平衡树:splay、treap
- 基于分裂合并的平衡树:非旋treap
- 基于重构的平衡树:替罪羊树
- 常用维护区间的平衡树:非旋treap,splay
- 其中有堆性质的平衡树可用笛卡尔建树,复杂度基于堆性质,期望\(O(\log{N})\),splay利用其\(splay(x,goal)\)操作,均摊复杂度也为\(O(\log{N})\),证明要用到势能分析,略去
link cut trees
- LCT是用来维护一个动态森林连通性的结构,支持link和cut
- 主要思想是利用实链剖分,将每个实链用一个splay维护,同时在splay中权值为在原树中的深度,值得注意的是每个节点的父节点
- 对于一个splay中的点,并不直接维护原树的父亲,而splay根的父节点为splay中dep最小的点的父节点
- 最主要的有\(access(x),makeroot(x),findroot(x),link(x,y),cut(x,y)\)等操作
- 主要基于\(access(x)\)操作即访问x节点,并将根节点(原树中)到x的路径变为重链,并x与root在同一splay中,单次均摊复杂度为\(O(\log{N})\)
- 应用有动态连通性,动态维护最小生成树,求LCA
思想
分治(cdq,整体二分,点分治,边分治)
- CDQ
- cdq分治在二维上的最常用的应用是归并排序
- cdq分治即用其分治的框架来将整个问题分开,分为两个子问题
- 并在做好了每个子问题后,计算跨过分治重心的对答案的贡献
- 利用sort(第一维)、cdq分治、数据结构维护(最后一维)可以解决多维偏序问题
- 其中,利用cdq分治套cdq分治套cdq分治...可以解决k维偏序问题,只要每一维分治的同时排序(类似归并),到下一维前要统计每一个数在前面每一维所在左边还是右边,在最后一维统计答案即可
- 故cdq本质即利用其分治框架,降维,而一些题中利用CDQ分治将动态问题变为静态问题就是将时间轴这一维降维了(在操作序列上分治,必须离线)
- 整体二分
- 整体二分简单应用就是利用在值域上二分答案,将操作序列分治,求解区间kth,可支持修改
- 其思想就是在答案值域上二分,考虑判定一些问题答案一定在一边(或操作只对一边的数会有影响),将整个问题分为子问题
- 点分治