20230329 6.1. 什么是图
什么是“图” (Graph)
- 表示“多对多”的关系
- 包含
- 一组顶点:通常用 V (Vertex) 表示顶点集合
- 一组边:通常用 E (Edge) 表示边的集合
- 边是顶点对:(v, w) ∈ E ,其中 v, w ∈ V
- 有向边 <v, w> 表示从v指向w的边(单行线)
- 不考虑重边和自回路
抽象数据类型定义
- 类型名称:图(Graph)
- 数据对象集:G(V,E)由一个非空的有限顶点集合V和一个有限边集合E组成。
- 操作集:对于任意图 G ∈ Graph,以及 v ∈ V, e ∈ E
- Graph Create():建立并返回空图;
- Graph InsertVertex(Graph G, Vertex v):将v插入G;
- Graph InsertEdge(Graph G, Edge e):将e插入G;
- void DFS(Graph G, Vertex v):从顶点v出发深度优先遍历图G;
- void BFS(Graph G, Vertex v):从顶点v出发宽度优先遍历图G;
- void ShortestPath(Graph G, Vertex v, int Dist[]):计算图G中顶点v到任意其他顶点的最短距离;
- void MST(Graph G):计算图G的最小生成树;
怎么在程序中表示一个图
邻接矩阵
邻接矩阵G[N][N] —— N个顶点从0到N-1编号
G[i][j] =
- 1 :若<vi,vj> 是G中的边
- 0 :其他情况
对于无向图的存储,怎样可以省一半空间?
用一个长度为N(N+1)/2的1维数组A存储{G(0,0),G(1,0),G(1,1),……,G(n-1,0),…,G(n-1,n-1)},则G(i,j)在A中对应的下标是:
\[i*(i+1)/2 + j
\]
邻接矩阵的好处:
- 直观、简单、好理解
- 方便检查任意一对顶点间是否存在边
- 方便找任一顶点的所有“邻接点”(有边直接相连的顶点)
- 方便计算任一顶点的“度”(从该点发出的边数为“出度”,指向该点的边数为“入度”)
- 无向图:对应行(或列)非0元素的个数
- 有向图:对应行非0元素的个数是“出度”;对应列非0元素的个数是“入度”
有什么不好?
- 浪费空间 —— 存稀疏图(点很多而边很少)有大量无效元素
- 对稠密图(特别是完全图)还是很合算的
- 浪费时间 —— 统计稀疏图中一共有多少条边
代码实现
public class ArrayGraph {
/**
* 节点个数
*/
int size;
int[][] edges;
}
邻接表
G[N]为指针数组,对应矩阵每行一个链表,只存非0元素
一定要够稀疏才合算
- 方便找任一顶点的所有“邻接点”
- 节约稀疏图的空间
- 需要N个头指针 + 2E个结点(每个结点至少2个域)
- 方便计算任一顶点的“度”?
- 对无向图:是的
- 对有向图:只能计算“出度”;需要构造“逆邻接表”(存指向自己的边)来方便计算“入度”
- 方便检查任意一对顶点间是否存在边?
- 不方便
public class LinkedGraph {
/**
* 节点个数
*/
int size;
LinkedGraphNode[] edges;
}