【考研--数据结构】 多对多数据结构——图

注:以下内容均为个人复习使用,不保证所述内容绝对严谨;
注:考研知识点相对基础,因此这里只做知识点合集,不保证内容详细。

Pre: Conception

G(V,E),点V,边E的集合为G,称之为 图(Graph)
n表示点数量,m表示边数量

连通图

从图上任意一点出发,都可以找到一条路径,使可以回到该点。(每一个点都在回路中)

完全图

能加上的边都加上,无重边。
有向完全图的节点个数为: n*(n-1)/2
无向完全图的节点个数为:n*(n-1)

连通分量

一个图的连通分量 = 最大连通子图的大小
有向图的强连通:即每对节点双向连通;n点图则至少需要n条边(一个环),最多可以有n*(n-1)条边(完全图)
有向图的弱连通:是一张连通图,但存在某一对节点单向连通(只能A到B,不能B到A);n点图则至少需要n-1条边(一个树),最多可以有n*(n-1)条边(完全图)
弱连通有向图 是 特殊的强连通有向图

性质

n个点的有向图形成连通图 最少需要 n条边 (一个环)
n个点的无向图形成连通图 最少需要n-1条边(一个树)
n个点的无向图最少需要 n*(n-1)/2 +1条边 才能保证 这是一个连通图。

一、图的存储

1. 邻接矩阵:

edge[a][b] 表示从点a到点b的边权
存储优点: 操作简单,O(1)查询,空间稳定O(n^2)
存储缺点: 有重边情况不适用,稀疏图(边较少时),需要连续内存地址(点过多时不适用)。
空间复杂度: O(n2)

考试易错点:区分题目问的是非零元素 还是 元素

※矩阵存图的特殊性质:

性质1 : 矩乘含义
如果A[i][j]为图G的邻接矩阵,且为01矩阵(只记录是否存在边)
由线代知识、矩阵乘法可以推证:
矩阵A2=A*A中 A2[i][j]表示 由点i到点j 步数为2的路径数量
延申: 矩阵An=矩阵A的n次幂,An[i][j]表示 步数为n的路径数量

Ps. 如果A[i][j]为图G邻接矩阵,且存储值为边长va
则仍用A的n次幂An[i][j]表示步长为n,点i到点j的最短距离。可通过Floyd等算法计算。

性质2:“适当的编号”
如果想 通过适当的编号 将 n个点的有向无环图的邻接矩阵 成为
上三角矩阵 或 下三角矩阵
编号方式为:
按拓扑序列顺序编号(下三角矩阵)
按拓扑序列逆序编号(上三角矩阵) 

2. 邻接表:

下为个人代码习惯

	struct ss{
		int to,nex,va;
	}edge[M];int head[N],ecnt;//同理可推得指针写法;指针写法更支持动态空间维护、可移植性更强。
	void add(int x,int y,int va){//单向图
		edge[++ecnt]=(ss){y,head[x],va};head[x]=ecnt;
	}

存储优点: 稀疏图适用、可存储有重边的图; 如果用指针写成链表形式,则内存地址不需要连续,增边O(1),动态空间占用,
存储缺点: 操作复杂,O(M)查询
空间复杂度: O(n + m)

3. 逆邻接表:即 邻接表 存 反向图

4. 邻接多重表:

即每个表元素同时存储两个节点,且链接在两个节点的链表中

struct ss{
	int from,to,va;
	struct ss *from_nex;
	struct ss *to_nex;
}*head[N];
inline void add(int x,int y,int va){
	struct ss *p = (struct ss*)malloc(sizeof(ss));
	p->from=x,p->to=y,p->va=va;
	p->from_nex=head[x];head[x]=p;
	p->to_nex=head[y];head[y]=p;
}

二、图的遍历

图的遍历的实质其实就是 找当前点的邻接点的过程

1. BFS遍历法: 宽度优先搜索

时间复杂度 O(n+e)
使用的数据结构:队列
空间复杂度:O(n)

2. DFS遍历法: 深度优先搜索

时间复杂度 O(n+e)
使用的数据结构:栈
空间复杂度:O(n)

三、算法问题——最小生成树(MST)

问题描述:给一张图,求其所有点都使用的情况下,可形成的边权总和最小的树。 MST算法的代码实现

法一:Prim算法

核心概括:(点集拓展+优先队列)

算法思路: 贪心(优先队列实现) + 判环(集合实现)

step1. 随机选取一个点不在集合内的点,并将该点连接的所有边加入优先队列,
step2. 选取优先队列的top(即集合当前所连接所有边中的最短边),将该边作为树的其中一个边,
step3. 将所连向的to_pos加入集合,如果该点已经在集合,则换下一条边,进行前述操作。
step4. 重复上述操作,直到所有点全部加入集合
注:推荐使用邻接矩阵。

算法复杂度:

O(N+logM) ⇒ 适用于密集图(边较多时)
补:考研的Prim是不加优先队列优化的prim,所以复杂度为O(n2)

法二:Kuraskal算法

核心概括:(边集补全+一次排序+并查集)

算法思路: 贪心(排序实现) + 判环(并查集实现)

step1. 将所有边排序
step2. 每次都选取最短的边,将该边作为树的边,
如果该边所连向的两个点 已经在同一并查集,则弃之不用
否则该边合法,选用该边,更新并查集
step4. 重复前述操作,直至n-1条边被选用,或直至所有边被遍历一遍
注:推荐使用邻接表

算法复杂度:

O(m logm) ⇒ 适用于稀疏图(边较少时)

最小生成树的性质:

最小生成树可能不唯一,但最小生成树的权值一定唯一
最小生成树唯一的 充分条件 树上各个边权的权值不同
充要条件 权值相同的边不属于可以相互替代的关系

四、算法问题——最短路

问题描述:顾名思义,求某两个点之间的最短路径 最短路模板链接

Floyd / Dijkstra:

算法思路: 用已经更新过的两点距离,更新答案。 Dis[i][j] 表示从点 i 到点 j 的最短距离,则 Dis[i][j]=min(Dis[i][j] , dis[i][k]+e[k][j]);
算法复杂度: 单源最短路:O(n ^ 2); 多源最短路:O(n ^ 3)
算法优缺点: 负权不可用,适合密集图(边多点少)
没什么用的性质: 各个节点出最终结果的顺序,一定是按照其最短距离值从小到大排序。(我也不知道这性质有啥用,反正考研考)
堆优化 把每条路径的抵达点即长度都放入优先队列;每次优先选取距离最近的点进行更新。
法二:堆优化Dijkstra算法 O(n logn)
法三:SPFA O(n logn ~ n2 ) Link to SPFA+大模拟的一个题
法四:A*算法
【考研只要求dijkstra,其余方法略。

Dijkstra最短路生成树

根据Dijkstra生成源点到各个顶点的最短路径,
这些最短路径的集合有且只有n-1条边,因此构成生成树。

五、AOV网 与 拓扑排序

Definition: 即,被研究的对象为V(点集)。
要求为无环图

拓扑排序: 单向流路径

拓扑序列指,按该序列排序的点集,对于任意 点 j , 均存在 i < j 使 点 i 到 点 j 有路径
应用:拓扑判环 : 如果有拓扑序列,则一定有环;如果没有,则无环。
寻找拓扑序列的操作:

  1. 入度为0 的点 为 第一个点。 如果入度为0的点不止一个,则无所谓先后顺序。
  2. 将第一个点以及他所连出的边删除,更新点的度数
  3. 重复1,直到点集为空;

注: 拓扑序列 可能 不唯一

BFS拓扑排序法

bool ind[N];//入度
int ans[N];//记录序列
bool Toplogical_sort(){
	queue<int>q; //用于存储当前入度为0 的点
	for(int i=1;i<=n;i++)
	if(!ind[i]) q.push(i);
	whlie(!q.empty()){
		int pos=q.front();q.pop(); ans[++tot]=pos;
		for(int i=head[pos];i;i=edge[i].nex){
			ind[ edge[i].to ] --;
			if(!ind[ edge[i].to ] ) q.push(edge[i].to);
		}
	}
	for(int i=1;i<=n;i++)
	if(ind[i]) return false;//有环
	return true;
}

DFS拓扑排序法

DFS+depth[pos]标记,最终排序顺序按depth从小到大即可。

拓扑排序求最长路径法

六、AOE网与关键路径

Definition: 即,被研究的对象为E(边)。
应用场景:流程图的总花费时间。

关键路径:该路径延长,则工期必延长(完成整个流程的最小花费时间 = 图内最长路径长度)

考虑到可以同时进行,所以总花费时间=max(路径长度)。这个max路径,即成为 关键路径

一般先通过拓扑排序实现 流程点顺序的先后排序。
再计算路径长度最大值。
项目的最早开始时间(即前缀项目的关键路径) = 起点到该项目的所有路径长度的maxn
项目的最晚开始时间(即不在关键路径上的项目可以根据情况延迟开始) = 整个流程的总时间 minus 起点到该项目到最后项目的路径长度的maxn

关键活动:该活动延迟,工期必延迟(即 最晚开始时间=最早开始时间 的活动)

七、零散考点

1. 有向无环图描述表达式

至少需要的顶点数目 = set ( 表达式所有元素 ) 的集合大小
即,对于相同的元素,只建立一个节点,以优化空间。
如: (A+B)*((A+B)/A) 只需要5个节点即可表示,节点信息分别为 “A、B、+、 *、/ ”。

2. 手跑dfs和bfs的遍历顺序;给定邻接表手绘图

3. 判环算法

BFS广搜+入队次数判环法(一个神奇的判环方法, 如果一个点的入队次数>n,那一定有环存在)
DFS深搜+vis[pos] 标记法(走到一个点则做一次标记,如果走到做过标记的点,即说明有环,只用于单纯判环)
直接求强连通分量(Tarjan算法) 【在判环的基础上,进行缩点操作/ 判断割点】

4. 图的度的关系

有向图: 出度和=入度和=总边数; 所有点度的和=出度和+入度和=2总边数
无向图:所有点的度和=2
边数
根据上述等式推算相关题目的 节点数量。

5. 图的DFS遍历等价于二叉树的先序遍历;图的BFS遍历等价于二叉树的层序遍历

6. 代码题

邻接表 与 邻接链表

	typedef struct{//邻接链表写法
		int to,va;
		STU *nex;
	}STU;	STU *head[N]void add(int x,int y,int va){//单向图
		STU *p = (STU *) malloc(sizeof(STU));
		p->nex=head[x];head[x]=p;
		p->to=y;p->va=va;
	}

邻接链表 中 元素的删除

(即单链表删除)

邻接矩阵 与 矩阵压缩

无向图的邻接矩阵一定是对称矩阵,则可以使用对称矩阵【顺序表】压缩存储法
有向图的稀疏图,可以用【三元组】【十字链表】压缩存储法
详见:线性数据结构

7. 最小高度生成树 :BFS即可

手绘BFS生成树 和 DFS生成树 的图
手绘最小生成树

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoesM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值