🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:数据结构与算法
🌠 首发时间:2022年11月13日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
🌟 一以贯之的努力 不得懈怠的人生
图中点和点之间是否有连接,我们可以用一个表来表示,其中 “1” 表示两点之间有边,反之

上图很容易用代码表示出来
#define MaxVertexNum 100 //顶点数目的最大值
typedef struct{
char Vex[MaxVertexNum]; //顶点表
int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum, arcnum; //图的当前顶点数和边数/弧数
} MGraph;
顶点的数组中我们可以存储更复杂的信息,比如一个结构体
结点数为 n n n 的图 G = ( V , E ) G = (V, E) G=(V,E) 的邻接矩阵 A A A 是 n × n n \times n n×n 的,将 G G G 的顶点编号为 v 1 , v 2 , … , v n v_1, v_2, \dots, v_n v1,v2,…,vn, 则
KaTeX parse error: No such environment: align* at position 8: \begin{̲a̲l̲i̲g̲n̲*̲}̲ \begin{split} …
在这种情况下,我们怎么求顶点的度、入度和出度呢?
邻接矩阵法求顶点的度 / 出度 / 入度的时间复杂度均为 O ( ∣ V ∣ ) O(|V|) O(∣V∣)
#define MaxVertexNum 100 //顶点数目的最大值
#define INFINITY 最大的int值 //宏定义常量 “无穷”
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上权值的数据类型
typedef struct{
VertexType Vex[MaxVertexNum]; //顶点
EdgeType Edge[MaxVertexNum][MaxVertexNum]; //边的权
int vexnum, arcnum; //图的当前顶点数和弧数
} MGraph;
空间复杂度: O ( ∣ V ∣ ) O(|V|) O(∣V∣) —— 只和顶点数相关,和实际的边数无关,邻接矩阵法采用数组顺序存储的形式,适合用于存储稠密图
无向图的邻接矩阵是对称矩阵,我们可以压缩存储,也就是只存储上三角区或者下三角区

设图 G G G 的邻接矩阵为 A A A(矩阵元素为 0 / 1 0/1 0/1),则 A n A^n An 的元素 A n [ i ] [ j ] A^n[i][j] An[i][j] 等于由顶点 i i i 到顶点 j j j 的长度为 n n n 的路径的数目
假设 A A A 如下表所示

现在要计算
A
2
[
1
]
[
4
]
,
A
2
[
2
]
[
2
]
A^2[1][4], A^2[2][2]
A2[1][4],A2[2][2],该怎么做呢?
A
2
[
1
]
[
4
]
=
a
11
×
a
14
+
a
12
×
a
24
+
a
13
×
a
34
+
a
14
×
a
44
=
1
A
2
[
2
]
[
2
]
=
a
21
×
a
12
+
a
22
×
a
22
+
a
23
×
a
32
+
a
24
×
a
42
=
3
A^2[1][4] = a_{11} \times a_{14} + a_{12} \times a_{24} + a_{13} \times a_{34} + a_{14} \times a_{44} = 1 \\ A^2[2][2] = a_{21} \times a_{12} + a_{22} \times a_{22} + a_{23} \times a_{32} + a_{24} \times a_{42} = 3
A2[1][4]=a11×a14+a12×a24+a13×a34+a14×a44=1A2[2][2]=a21×a12+a22×a22+a23×a32+a24×a42=3
其中
a
11
a_{11}
a11 表示矩阵的第
1
1
1 行第
1
1
1 个元素,其他依此类推
我们画出 A 2 A^2 A2 的表,如下

可以发现,我们计算出来的值就是这个表中对应位置的值
如果让你计算 A 3 [ 1 ] [ 4 ] A^3[1][4] A3[1][4] 呢?

// “边/弧”
typedef struct ArcNode{
int adjvex; //边/弧指向哪个结点
struct ArcNode *next; //指向下一条弧的指针
InfoType info; //边权值(没有可不加)
}ArcNode;
// “顶点”
typedef struct VNode{
VertexType data; //顶点信息
ArcNode *first; //第一条边/弧
}VNode, AdjList[MaxVertexNum];
//用邻接表存储的图
typedef struct{
AdjList vertices;
int vexnum, arcnum;
}ALGraph;
当然,有向图也可以用邻接表法来表示

对于无向图,由于每一条边都会有一个指向的结点占空间,而且是双向的,比如
A
A
A 和
B
B
B 之间有一条边,那么
A
A
A 到
B
B
B 这条边就会有一个指向
B
B
B 的结点,同理
B
B
B 到
A
A
A 这条边就会有一个指向
A
A
A 的结点,由于边结点的数量是
2
∣
E
∣
2|E|
2∣E∣ ,所以整体的空间复杂度为
O
(
∣
V
∣
+
2
∣
E
∣
)
O(|V| + 2|E|)
O(∣V∣+2∣E∣)
对于有向图,由于每条边都是有方向的,所以只会有一个指向结点,整体的空间复杂度即为 O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣)
那么,下面我们思考一下怎么求顶点的度、入度和出度呢?
图的邻接表表示方式并不唯一,与一个顶点有关系的边的顺序是可以颠倒排列的;但是对于前面的邻接矩阵来说,只要确定了顶点编号,那图的邻接矩阵就是唯一的
| 对比角度 | 邻接矩阵 | 邻接表 |
|---|---|---|
| 空间复杂度 | O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2) | 无向图 O ( ∣ V ∣ + 2 ∣ E ∣ ) O(|V| + 2|E|) O(∣V∣+2∣E∣);有向图 O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣) |
| 适合用于 | 存储稠密图 | 存储稀疏图 |
| 表示方式 | 唯一 | 不唯一 |
| 计算度 / 出度 / 入度 | 必须遍历对应行或列 | 计算有向图的度、入度不方便,其余很方便 |
| 找相邻的边 | 必须遍历对应行或列 | 找有向图的入边不方便,其余很方便 |
我们需要定义两个结构:弧结点和顶点结点

空间复杂度为: O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣)
如何找到指定顶点的所有出边? —— 顺着绿色线路找
如何找到指定顶点的所有入边? —— 顺着橙色线路找
注意:十字链表只用于存储有向图

空间复杂度为: O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣)
删除边、删除节点等操作很方便
注意:邻接多重表只用于存储无向图
| 角度 | 邻接矩阵 | 邻接表 | 十字链表 | 邻接多重表 |
|---|---|---|---|---|
| 空间复杂度 | O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2) | 无向图 O ( ∣ V ∣ + 2 ∣ E ∣ ) O(|V| + 2|E|) O(∣V∣+2∣E∣);有向图 O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣) | O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣) | O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(∣V∣+∣E∣) |
| 找相邻边 | 遍历对应行或列,时间复杂度为 O ( ∣ V ∣ ) O(|V|) O(∣V∣) | 找有向图的入边必须遍历整个邻接表 | 很方便 | 很方便 |
| 删除边或顶点 | 删除边很方便,删除顶点需要移动大量数据 | 无向图中删除边或顶点都不方便 | 很方便 | 很方便 |
| 适用于 | 存储稠密图 | 存储稀疏图或其他 | 只能存储有向图 | 只能存储无向图 |
| 表示方式 | 唯一 | 不唯一 | 不唯一 | 不唯一 |