1、什么是图
表示多对多的关系
包含一组顶点:通常用V(Vertex)表示顶点集合
一组边:通常用E(Edge)表示边的集合
2、抽象数据类型定义
类型名称:图(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的最小生成树;
3、用邻接矩阵表示一个图
邻接矩阵G[N][N]——N个顶点从0到N-1编号
G[i][j] =1 若<vi,vj>是G中的边 ,否则,G[i][j] =0
Q:对于无向图的存储,怎样可以省一半空间?
A:
用一个长度为N(N+1)/2的1维数组A存储 {G00,G10,G11,......,Gn-1 0,...,Gn-1 n-1},
则Gij在A中对应的下标是: (i*(i+1)/2+j)
对于网络,只要把G[i][j]的值定义为边<vi ,vj >的权重即可。
邻接矩阵的好处:
直观、简单、好理解
方便检查任意一对顶点间是否存在边
方便找任一顶点的所有“邻接点”(有边直接相连的顶点)
方便计算任一顶点的“度”(从该点发出的边数为“出 度”,指向该点的边数为“入度”)
(无向图:对应行(或列)非0元素的个数 ,有向图:对应行非0元素的个数是“出度”;对应列非0元素的 个数是“入度” )
邻接矩阵的坏处:
浪费空间 —— 存稀疏图(点很多而边很少)有大量无效元素
浪费时间 —— 统计稀疏图中一共有多少条边
4、用邻接表表示一个图
方便找任一顶点的所有“邻接点”
节约稀疏图的空间(需要N个头指针 + 2E个结点(每个结点至少2个域) )
对无向图来说是方便计算任一顶点的度的,对有向图来说只能计算出度,需要构造“逆邻接表”(存指向自己 的边)来方便计算“入度”
不方便检查任一顶点的是否存在边
5、图的遍历
深度优先搜索
类似于树的先序遍历
void DFS ( Vertex V ) { visited[ V ] = true; for ( V 的每个邻接点 W ) if ( !visited[ W ] ) DFS( W ); }
若有N个顶点、E条边,时间复杂度是 :
用邻接表存储图,有O(N+E)
用邻接矩阵存储图,有O(N^2)
广度优先搜索
??void BFS ( Vertex V ) { visited[V] = true; Enqueue(V, Q); while(!IsEmpty(Q)){ V = Dequeue(Q); for ( V 的每个邻接点 W ) if( !visited[W] ) { visited[W] = true; Enqueue(W, Q); } } }
若有N个顶点、E条边,时间复杂度是 :
用邻接表存储图,有O(N+E)
用邻接矩阵存储图,有O(N^2)
6、图不连通怎么办
相关概念:
连通:如果从V到W存在一条(无向)路径,则称 V和W是连通的
路径:V到W的路径是一系列顶点{V, v1, v2, ..., vn, W}的集合,其中任一对相邻的顶点间都有图
中的边。路径的长度是路径中的边数(如果带 权,则是所有边的权重和)。如果V到W之间的所有顶点都不同,则称简单路径
回路:起点等于终点的路径 连通图:图中任意两顶点均连通
连通图:图中任意两顶点均连通
连通分量:无向图的极大连通子图 (极大顶点数:再加1个顶点就不连通了 极大边数:包含子图中所有顶点相连的所有边 )
对于有向图我们分强连通和弱连通的概念。
强连通:有向图中顶点V和W之间存在双向路 径,则称V和W是强连通的
强连通图:有向图中任意两顶点均强连通
强连通分量:有向图的极大强连通子图