前言:本文内容均整理自学堂在线《数据结构》(上/下)课程的视频、课件(特别棒!免费学习,还有专业回答),仅用于个人概念复习和思路回顾。对学堂在线及相关人员,特别是邓公,表示衷心感谢!
摘要:这里主要包括(1)算法概述(2)排序和查找(3)四种结构(4)图的概念和算法,此外还有BST专题篇,串匹配专题篇,以及其他高级数据结构(课件里还好多美味!)
算法概述
算法:特定计算模型下,旨在解决特定问题的指令序列(输入,输出,正确性,确定性,可行性,有穷性)
Turing Machine(列表), Random Access Machine(向量)
大O记号(上界),大θ记号(确界),大Ω记号(下界)
幂方级数:比幂次高出一阶(1+2+3+4……)
几何级数:与末项同阶(1+2+4+8……)
收敛级数:O(1), 1+1/4+1/9+1/16……
调和级数:1+1/2+1/3+1/4…… = Θ(logn)
对数级数:log1+log2+log3+log4…… = Θ(nlogn)
封底估计 back-of-the-envelope calculations (BotEC):
一天≈10^5秒;1生≈1世纪≈3X10^4天;三生三世 ≈ 300 yr = 10^10 秒
普通PC:1GHz,10^9 flops
排序和查找
冒泡排序, swap one by one
冒泡排序的优化:记录最后一次冒泡位置,使得输入敏感。
选择排序,record the max (or min) position, swap once
循环节:每一步迭代,元素所属循环节长度恰好减少一个单位;该元素脱离原属循环节,自成长度为1的循环节;有多少个循环节,就出现几次无需swap的情况,最大值为n,期望值为θ(logn)
堆排序,heap + select_sort
建堆,不断delMax():交换--下滤;(不稳定)
不需全排序,找出前K个词条 O(klogn)的select_sort
插入排序,put one into sorted sequence,
输入敏感性
逆序对:每次比较次数为逆序对数量+1
归并排序,put one(in a sorted sequence) into sorted sequence.(稳定)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 void merge_sort(int *s, int p, int n){ 2 // sort from s[p] to s[p+n-1] 3 if (n < 2) return; 4 int mi = n >> 1; 5 int *temp = new int[mi]; 6 for (int i = 0; i < mi; ++i) { 7 temp[i] = s[p+i]; 8 } 9 merge_sort(temp, 0, mi); 10 merge_sort(s, p+mi, n-mi); 11 int i = 0, j = p+mi, t = p; 12 while (i < mi) { // till temp[] is empty. 13 s[t++] = (j == p + n || temp[i] < s[j]) ? temp[i++] : s[j++]; 14 } 15 delete[] temp; 16 }
快速排序
不稳定,就地O(1)空间,LUG版本(从左右向中间扫描)和LGU版本(从左到右扫描)
构造轴点 pivot(k选取问题),linearSelect算法选取中位数,渐进O(n)
希尔排序
步长序列shell sequence(g,h,… ordered 会不断积累>>推论(g,h)的线性组合都是有序的)
例如PS序列:{2^k-1};pratt’s Sequence,{1,2,3,4,6,8,9,12…};Sedgewick Sequence:实践最佳的
实现:用by-rank的向量和输入敏感的排序算法(如插入排序)。
桶排序 bucket sort,也称基数排序 radix sort【不是基于比较的排序】,体现分而治之的策略
整数排序 :数量/值域 符合常对数密度 log(n)/log(n^d) ,问题转化为d个域的桶排序问题。
计数排序:小集合+大数据:统计每个桶数量--累加桶数量--扫描每个数据填补对应秩(稳定性处理)
二分查找, 简洁版(时间复杂度必定为logn)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int bin_search(int *s, int tar, int lo, int hi) { 2 3 // search tar in s[lo] to s[hi], return the index: s[index] <= tar < s[index + 1] 4 while (lo < hi) { 5 int mi = (lo + hi) >> 1; 6 (tar < s[mi]) ? hi = mi : lo = mi + 1; 7 } 8 return --lo; 9 }
众数查找
中位数查找
四种结构
向量-静态操作,by rank 》》栈 stack 队列 queue 优先级队列 heap
列表-动态操作, by position
树-层次结构,by key 兼顾vector和list的优点:高效查找、插入、删除,半线性结构
散列,by value 关键码之间只需支持比对判等,不必支持大小比较
栈的典型应用场景:
栈混洗(个数 = Catalan数)
逆波兰表达式(RPN)
path(v) – v – subtree(v) :节点的深度|path(v)|,树的高度(空树为-1)max(|subtree(v)|)
真二叉树(proper binary tree,所有节点度数为2) huffman编码树
完全二叉树(向量存储)
长子-兄弟表示法(多叉树--二叉树转化)
树的先序遍历,中序遍历,后序遍历,层次遍历(根据根节点访问的位置)
pre-order traversal: root node >> left child >> right child
in-order traversal: left child >> root node >> right child
post-order traversal: left child >> right child >> root node ==RPN
散列表 hash table/桶数组 bucket array
散列函数:确定 determinism,快速 efficiency, 满射 surjection,均匀 uniformity
除余法:hash(key) = key % M 。 M素数;缺陷:不动点,零阶均匀。
MAD: hash(key) = (a*key+b)%M。Multiply-Add-Divide 一阶均匀。多项式法
数字分析 selecting deigits;平方取中 mid-square;折叠法 folding;位异或法 XOR;
(伪)随机数法 hash(key) = rand(key) = [rand(0) * a ^ key] % M :可移植性差
closed hashing:多槽位法,独立链法 linked-list chaining ,公共溢出区(顺序存储冲突数据)
开放定址 open addressing(任何散列都可以接纳任何词条):线性试探;双向平方试探(表长取4k+3的素数)
装填因子,小于50%;重散列。
懒惰删除:仅作删除标记;查找:while (ht[r] && (k!=ht[r]->key) || !ht[r] && lazilyRemoved(r))
优先级队列 priority queue:向量实现的完全二叉树,堆
三个接口:delMax(), insert(), merge()
(保持结构性和堆序性)插入--至最后一个节点--上滤;删除--与最后一个节点交换--下滤。
Floyd建堆:自下而上的下滤(合并子树),O(n)
锦标赛树:N叶子,O(N)次两两比较,决出胜者(N空间记录);更新(上一优胜者的祖先进行logN次比较);渐进意义上与效率相等,常数意义上更小。
多叉堆d-heap:基于向量实现parent(k) = math.floor((k-1)/d), child(k,i) = kd+i;降低了堆高,使得上滤成本降低,下滤成本增加。图算法(Prim和Dijkstra)中,d=e/n+2时,性能最优。不能借助移位加速秩的换算,特别适用于大规模跨越存储层次时。
左式堆:npl(x) = 1+min{ npl(x.lc), npl(x.rc) } 到外部节点的最近距离。主要用于解决堆合并问题(效率Ologn),借助二叉树结构和heap方法。
图的概念及算法
vertex 顶点 tail 尾 head 头
edge 边 undirected edge 无向边 directed edge 有向边
adjacency 邻接 self-loop 自环 incidence 关联
degree/valency 度
simple graph 简单图(无重边和自环)
undigraph 无向图 digraph 有向图 mixed graph 混合图 DAG(Directed Acyclic Graph) 有向无环图
欧拉环路(边);哈密尔顿环路(顶点)
spanning tree 支撑树 weighted network 带权网络 MST(minimum spanning tree) 最小支撑树
adjacency matrix 邻接矩阵(点和点),incidence matrix 关联矩阵(点和边),邻接表
planar graph 平面图 Euler’s formula:v-e+f-c = 1
sparse graph 稀疏图
BFS(Breadth-First Search)
DFS(Depth-First Search) 深度优先搜索 活跃期:dTime, fTime
图的算法(根据讲稿整理,主要思路实现)
拓扑排序:有向无环图DAG的排序
数据结构:
node[] queue(记录排列结果)
int[] vector_fronts(记录入度变化)
邻接矩阵(vector * 单向list,记录关系)
算法:BFS
初始化queue(入度为0的点),出queue,减入度,if 入度为0,进queue。
prim 算法:无向图求最小支撑树 MST
数据结构:
node[] vector_parent(记录顶点到MST的最小跨边对应的顶点)
bool[] vector_flag(记录顶点是否纳入MST中)
邻接矩阵(记录关系)
算法:贪心 + PFS(优先级遍历) O(n2)
每次 (1) 在 vector_parent( not in MST ) 中选择一条极短跨边 shortest crossing edge,(2) vector_flag中标记node加入MST,(3) vector_parent( not in MST )中到node的边更短?更新:不更新。
Dijkstra 算法:连通图求单点源(single-source)到其他顶点的最短路径 (Shortest Path Tree)
要求:边权值非负
数据结构:
node[] vector_parent (记录顶点到SPF的最少代价对应的顶点)
int[] vector_cost (记录顶点到SPF的最少代价)
bool[] vector_flag (记录顶点是否纳入SPF中)
算法:贪心 + PFS
每次 (1) 在vector_parent( not in SPF ) 中选择代价最小的顶点,(2) vector_flag 标记node加入SPF,(3) vector_parent( not in SPF )中转自node的代价更小?更新cost代价和parent父节点标记:不更新
双联通分量
关节点 articulatioin point, cut-vertex :删掉后原图连通分量增多(一个连通图变成多个子连通图)
双(重)连通图 bi-connectivity :无关节点的图
极大双连通子图,双连通分量 bi-connected components
数据结构:栈存放已访问的边
算法:DFS, O(n+e)
用dtime和hca(Highest Connected Ancestor)记录DFS遍历次序,tarjan算法
Kruskal 算法:无向图求最小支撑树(功能同prim算法)
将所有边组成优先队列(eloge),每次选最短边(不构成环的),迭代n-1次结束。【用并查集检测环路】
稀疏图时e≈n,效率比prim算法高
Floyd-Warshall 算法:连通图中所有点对的最短距离
算法1:每个顶点用Dijkstra算法,O(n3)
算法2:FW,也是O(n3),允许负权边,单不能有负权环路。动态规划,代码简单
三重循环:中间节点k, 两边节点i, j