week3
408
算法
18.二叉树
二叉树的顺序存储的常考点:
- i的左孩子:2i
- i的右孩子:2i+1
- i的父节点:⌊i/2⌋\left \lfloor i/2 \right \rfloor⌊i/2⌋
- i所在的层次:⌈log2n+1⌉\left \lceil \log_{2}{n+1} \right \rceil⌈log2n+1⌉
链式存储:

n个结点的二叉链表共有n+1个空链域
三叉链表:在原先的基础上加上父节点指针
二叉树的遍历:
- 先序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
通过其中两个遍历的结果推算二叉树的构造是考试的重点
先序遍历算法:

层次遍历:一层一层遍历二叉树
算法:

思路:
- 初始化一个辅助队列
- 根结点入队
- 若队列非空,则队头结点出队,访问该结点,将其左右孩子入队
- 重复3直到队空
19.线索二叉树

分前中后序遍历,线索就是将前驱和后继记录下来
线索二叉树的作用就是方便找前驱和后继
中序线索化:

先序线索化:


20.树的存储方式
①双亲表示法:顺序存储,用数组和数组长度来记录
缺点:找孩子不方便,要遍历整个数组
②孩子表示法:顺序存储+链式存储。用数组顺序存储各个结点,每个结点中保存数据元素、孩子链表头指针
缺点:找双亲不方便
③孩子兄弟表示法:每个结点保存数据、指向第一个孩子的指针、指向右边一个兄弟的指针

上述三个存储方法都可以存储森林
21.树、森林、二叉树的转换
树->二叉树:
- 先在二叉树中,画一个根节点
- 按孩子兄弟表示法依次处理每个结点

森林->二叉树:
- 先把所有树的根节点画出来,在二叉树中用右指针串成糖葫芦
- 按"森林的层序"依次处理每个结点

二叉树->树:
- 先画出树的根节点
- 从树的根节点开始,按"树的层序"依次处理每个结点
二叉树->森林:
- 先把二叉树的根节点和"一整串右指针糖葫芦"拆下来,作为多棵树的根节点
- 按"森林的层序"依次恢复每个结点的孩子
本质:用孩子兄弟表示法存储树或者森林,在形态上与二叉树类似
用孩子兄弟表示法存储森林时,将森林的每棵树的根节点视为平级的兄弟关系
22.树和森林的遍历
树:先根遍历、后根遍历、层序遍历

森林:先序遍历、中序遍历、后序遍历(等效于对用孩子兄弟表示法表示的森林进行相应遍历)
23.哈夫曼树
结点的权:每个结点自身所带的数值
结点的带权路径长度:从树的根到该结点的路径长度(经过的边数)与该结点上权值的乘积(只计算叶子结点)
WPL=∑i=1nwi∗liWPL = \sum_{i=1}^{n} w_i*l_iWPL=∑i=1nwi∗li

在含有n个带权叶结点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树(最优二叉树)

构造哈夫曼树:
- 将这n个结点分别作为n棵仅含一个结点的二叉树,构成森林F
- 构造一个新结点,从F中选最小的两个结点作为新节点的左右子树,并且将新结点的权值改为左右子树根结点的权值之和
- 从F中删除刚选出的两棵树,同时将新得到的树加入F
- 重复2、3,直到F中只剩下一棵树

哈夫曼树的结点总数为2n-1
哈夫曼树中不存在度为1的结点
哈夫曼树不唯一,但是WPL必然同样为最优
哈夫曼编码:
根据英文字母的使用频率,将其构造成一个哈夫曼树,左子树为0右子树为1,这样构造出来的编码可以减少电报的长度
相关概念:
固定长度编码:每个字符用相等长度的二进制位表示
可变长度编码:每个字符用可变长度的二进制位表示
若没有一个编码是另一个编码的前缀,则称这样的编码是前缀编码

24.并查集
并查集:集合的一种特殊形式,只需要合并和查找两种操作

find操作的优化(压缩路径,对多次查找有优化):

优化后,find操作的时间复杂度(多次查找):O(α(n)),union操作的时间复杂度:O(log2(n))
25.图
图由顶点集V和边集E组成,记为G=(V, E),E(G)表示G中顶点之间的关系集合,|V|表示图G中顶点的个数,也称图G的阶,用|E|表示图G中边的条数
注意:线性表可以是空表,树可以是空树,但图一定不为空
无向图:E是无向边的集合,有向图:E是有向边的集合
简单图、多重图:

对于无向图:顶点v的度是指依附于该顶点的边的条数
对于有向图:分为入度(ID)和出度(OD),顶点的度(TD)为入度与出度之和
路径:顶点v1到vn的一条路径是指顶点序列
回路:v1和vn是同一个点
简单路径:顶点不重复
简单回路:同上
路径长度:路径上边的数目
点到点的距离:若最短路径存在,则此路径的长度称为距离;若不存在,则距离为∞
无向图中,若顶点v和顶点w有路径存在,则v和w是连通的
有向图中,若顶点v到顶点w和顶点w到顶点v之间都有路径,则v和w是强连通的
无向图中,若任意两个顶点都是连通的,则是连通图
有向图中,若任意两个顶点都是强连通的,则是强连通图
常见考点:
- G是无向连通图,则最少有n-1条边
- G是无向非连通图,则最多有Cn−12C^2_{n-1}Cn−12条边
- G是强连通图,则最少有n条边
若G’与G的顶点完全相同,则称G’为G的生成子图
无向图中的极大连通子图(包含尽可能多的顶点和边)称为连通分量
有向图中的极大强连通子图称为有向图的强连通分量
连通图的生成树是包含图中全部顶点的一个极小连通子图(边要尽可能的少,但要连通)
非连通图中,连通分量的生成树构成了非连通图的生成森林
边的权:边自身携带的数值
带权图:边上有权的图
带权路径长度:带权图中,一条路径上所有边的权值之和
无向完全图:任意两个顶点之间存在边
有向完全图:任意两个点都强连通
树:不存在回路,且连通的无向图
有向树:一个顶点的入度为0,其余为1的有向图
邻接矩阵表示法:1表示邻接
如果是有向图,第i个结点的度=第i行的非零元素个数+第i列非零元素的个数
无向图的邻接矩阵是对称的,因此可以用压缩矩阵的方法压缩存储
性质:图G的邻接矩阵为A,则AnA^nAn的元素An[i][j]A^n[i][j]An[i][j]等于顶点i到顶点j的长度为n的路径的数目
缺点:空间复杂度高,只适合存储稠密图
邻接表表示法:

分别存储元素的信息和与元素相接的元素的指针
缺点:计算入度、出度不方便,只适合存储稀疏图
十字链表法:

缺点:只能存储有向图
邻接多重表法:

缺点:只能存储无向图
图的基本操作:

<x, y>表示有向弧,(x, y)表示无向弧
26.BFS广度优先搜索

空间复杂度(没说默认最坏情况):O(|V|)
对于用邻接矩阵存储的图,时间复杂度是O(|V|^2)
对于用邻接表存储的图,时间复杂度是O(|V|+|E|)
广度优先生成树:根据BFS的搜索过程产生的生成树
邻接矩阵的广度优先生成树是唯一的,而邻接表的广度优先生成树是不唯一的
27.DFS深度优先搜索
DFS与先序遍历类似:

空间复杂度(没说默认最坏情况):O(|V|)
对于用邻接矩阵存储的图,时间复杂度是O(|V|^2)
对于用邻接表存储的图,时间复杂度是O(|V|+|E|)
深度优先生成树:根据DFS的搜索过程产生的生成树
28.最小生成树
①Prim算法:从最小的顶点开始构建生成树,每次将代价最小的顶点纳入生成树,直到所有顶点都纳入为止

从农村开始,顺序是35142
②Kruskal算法:每次选择一条权值最小的边,使这条边的两头连通(原本连通的就不选),直到所有结点都连通

顺序是12345,学校和矿场连通,所以不选这个5,而是农场和P城之间的5
29.最短路径问题
①在进行BFS的时候顺便算出路径的长度:

时间复杂度:O(|V|^2)或O(|V|+|E|)
缺点:不适用于带权图
②Dijstrak算法:
- 循环遍历所有点,找到还没确定最短路径,且dist(distance)最小的顶点Vi,令final[i]=true
- 检查所有邻接自Vi的顶点,若其final值为false,则更新dist和path信息



时间复杂度:O(|V|^2)
③floyd算法:
- 初始化:不允许中转的情况下,算出最短路径的矩阵,并将中转矩阵path设为-1
- 若A(k−1)>A(k−1)[i][k]+A(k−1)[k][j]A^{(k-1)} > A^{(k-1)}[i][k] + A^{(k-1)}[k][j]A(k−1)>A(k−1)[i][k]+A(k−1)[k][j]
- 则A(k)=A(k−1)[i][k]+A(k−1)[k][j]A^{(k)} = A^{(k-1)}[i][k] + A^{(k-1)}[k][j]A(k)=A(k−1)[i][k]+A(k−1)[k][j],path(k)=kpath^{(k)} = kpath(k)=k
- 否则A(k)A^{(k)}A(k)和path(k)path^{(k)}path(k)保持原值
手算比较复杂,但代码很简单:

时间复杂度:O(|V|^3)
优点:能计算带负权值的图
30.有向无环图(DAG)描述表达式

图中的有向无环图有可以合并的部分
找到最少顶点的有向无环图的方法:
- 把各个操作数不重复地排一排
- 标出各个运算符的生效顺序
- 按顺序加入运算符(注意分层)
- 从底向上逐层检查同层的运算符是否可以合体
最简的图:

有向无环图的最优解也不唯一
31.拓扑排序
AOV网:用顶点表示活动的网,是有向无环图的一种
拓扑排序:
- 从AOV网中选择一个没有入度为0的顶点并输出
- 从王忠删除该顶点和所有以它为起点的有向边
- 重复1和2直到AOV网为空

拓扑排序也不唯一
代码实现:

(不一定要用栈,队列或其它也可以)
逆拓扑排序:
- 从AOV网中选择一个出度为0的顶点并输出
- 从王忠删除该顶点和所有以它为终点的有向边
- 重复1和2直到AOV网为空
32.关键路径
AOE网:顶点表示事件,有向边表示完成该事件的活动的开销的带权有向图
只有完成事件A的所有前驱事件,才能进行事件A
源点:开始顶点,汇点:结束顶点
从源点到汇点的所有路径中长度最大的就是关键路径,关键路径上的活动称为关键活动
求关键路径:
- 求所有事件的最早发生时间ve():拓扑排序后,ve(源点)=0,ve(k)=Max{ve(j)+Weight(vj,vk)}(vj为vk的任意前驱)
- 求所有时间的最迟发生时间vl():逆拓扑排序后,vl(汇点)=ve(汇点),vl(k)=Min{vl(j)-Weight(vk,vj)}(vj为vk的任意后继)
- 求所有活动的最早发生时间e():e(i) = ve(k)(<vk,vj>表示活动ai)
- 求所有活动的最迟发生时间l():l(i)=vl(j)-Weight(vk,vj)(<vk,vj>表示活动ai)
- 求所有活动的时间余量d():d(i)=l(i)-e(i)
- 时间余量为0的就是关键活动:d(i)=0
若关键活动耗时增加,则整个工程的工期将增长
缩短关键活动的时间,可以缩短整个工程的工期
当缩短到一定程度时,关键活动可能会变成非关键活动
33.查找算法
相关概念:
查找表:用于查找的数据集合
关键字:数据元素中唯一标示该元素的某个数据项的值
查找长度:对比关键字的次数
平均查找长度(ASL):所有查找过程中进行关键字比较的平均次数
①顺序查找:一个一个找
时间复杂度:O(n)
用查找判定树分析ASL:

若查找成功,则ASL成功=1+2+...+nn=n+12ASL_{成功} = \frac{1+2+...+n}{n}= \frac{n+1}{2}ASL成功=n1+2+...+n=2n+1
ASL失败=n+1ASL_{失败} = n + 1ASL失败=n+1
如果优化一下,将查找表排序,则ASL失败=1+2+...+n+nn+1=n2+nn+1ASL_{失败} = \frac{1+2+...+n+n}{n+1}= \frac{n}{2} + \frac{n}{n+1}ASL失败=n+11+2+...+n+n=2n+n+1n