图的邻接矩阵表示
2017-04-11 13:24
302 查看
图的邻接矩阵表示
图基概念(Graph)
包含一组顶点:通常用V (Vertex) 表示顶点集合
一组边:通常用E (Edge) 表示边的集合
边是顶点对:(v, w) ∈E ,其中v, w ∈ V有向边 表示从v指向w的边(单行线)不考虑重边和自回路
无向图:边是无向边(v, w)
有向图:边是有向边
连通:如果从V到W存在一条(无向)路径,则称V和W是连通的
连通图(Connected Graph):如果对于图的任一两个顶点v、w∈V,v和w都是连通的,则称该图为连通图。图中任意两顶点均连通。
连通分量(Connected Component):无向图中的极大连通子图。
极大顶点数:再加1个顶点就不连通了
极大边数:包含子图中所有顶点相连的所有边
强连通:有向图中顶点V和W之间存在双向路径,则称V和W是强连通的。
强连通图:有向图中任意两顶点均强连通。
强连通分量:有向图的极大强连通子图。
路径:V到W的路径是一系列顶点{V, v1, v2, …,vn, W}的集合,其中任一对相邻的顶点间都有图中的边。路径的长度是路径中的边数(如果带权,则是所有边的权重和)。
如果V到W之间的所有顶点都不同,则称简单路径
回路:起点等于终点的路径
邻接矩阵
图的邻接矩阵存储方式就是用一个二维数组来表示。邻接矩阵G——N个顶点从0到N-1编号顶点i、j有边,则G[i][j] = 1 或边的权重
邻接矩阵的优点
直观、简单、好理解
方便检查任意一对顶点间是否存在边
方便找任一顶点的所有“邻接点”(有边直接相连的顶点)
方便计算任一顶点的“度”(从该点发出的边数为“出度”,指向该点的边数为“入度”)
无向图:对应行(或列)非0元素的个数
有向图:对应行非0元素的个数是“出度”;对应列非0元素的个数是“入度”
邻接矩阵的缺点
浪费空间—— 存稀疏图(点很多而边很少)有大量无效元素
对稠密图(特别是完全图)还是很合算的
浪费时间—— 统计稀疏图中一共有多少条边
BFS广度优先搜索(Breadth First Search, BFS)
运用队列,将顶点V的每个邻接点进队。(类似于树的层先遍历)若有N个顶点、E条边,时间复杂度是
用邻接表存储图,有O(N+E)
用邻接矩阵存储图,有O(N^2)
DFS深度优先搜索索(Depth First Search, DFS)
用递归(类似于树的先序遍历)。ListComponents 图不连通时,列出各连通分量。
若有N个顶点、E条边,时间复杂度是
用邻接表存储图,有O(N+E)
用邻接矩阵存储图,有O(N^2)
深度优先遍历算法的非递归实现需要了解深度优先遍历的执行过程,设计一个栈来模拟递归实现中系统设置的工作栈,算法的伪代码描述为:
假设图采用邻接矩阵作为存储结构,具体算法如下:
测试代码:
/*! * \file 图的邻接矩阵表示.cpp * * \author ranjiewen * \date 2017/04/10 23:20 * * */ #include <iostream> #include <cstdio> #include <cstdlib> #include <queue> using namespace std; /* 图的邻接矩阵表示法 */ #define MaxVertexNum 100 /*最大顶点数设为100*/ #define INFINITY 65535 /*设为双字节无符号整数的最大值为65535*/ typedef int Vertex; /*用顶点下标表示顶点,为整型*/ typedef int WeightType; /*边的权值设为整型*/ typedef char DataType; /*顶点存储的数据类型设为字符型*/ /*边的定义*/ typedef struct ENode* PtrToENode; struct ENode { Vertex V1, V2; //有向边<v1,v2> WeightType Weight;//权重 }; typedef PtrToENode Edge; /*图结点的定义*/ typedef struct GNode *PtrToGNode; struct GNode { int Nv; //顶点树 int Ne; //边数 WeightType G[MaxVertexNum][MaxVertexNum]; //邻接矩阵 DataType Data[MaxVertexNum];// 存顶点的数据 //注意:很多情况下,顶点无数据,此时Data[]可以不用出现 }; typedef PtrToGNode MGraph; /*用邻接矩阵存储的图类型*/ bool Visited[MaxVertexNum] = { false }; MGraph CreateGraph(int VertexNum) { /*初始化一个有VertexNum个顶点但没有边的图*/ Vertex V, W; /*顶点的下标*/ MGraph Graph; Graph = (MGraph)malloc(sizeof(struct GNode)); /*建立图*/ Graph->Nv = VertexNum; Graph->Ne = 0; //初始化邻接矩阵 //注意:这里默认顶点编号从0开始到(Graph->Nv - 1) for (V = 0; V < Graph->Nv;V++) { for (W = 0; W < Graph->Nv;W++) { Graph->G[V][W] = INFINITY; } } return Graph; } void InsertEdge(MGraph Graph,Edge E) { //插入边<v1,v2> Graph->G[E->V1][E->V2] = E->Weight; //若是无向图,还要插入边<v2,v1> Graph->G[E->V2][E->V1] = E->Weight; } MGraph BuildGraph() { MGraph Graph; Edge E; Vertex V; int Nv, i; scanf("%d", &Nv); /*读入顶点个数*/ Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */ scanf("%d", &(Graph->Ne)); /*读入边数*/ if (Graph->Ne!=0) //如果有边 { E = (Edge)malloc(sizeof(struct ENode)); //建立边结点 //读入边,格式为:起点,中点,权重;插入邻接矩阵 for (i = 0; i < Graph->Ne;i++) { scanf("%d %d %d", &E->V1, &E->V2, &E->Weight); //注意:如果权重不是整型,weight的读入格式要改变 InsertEdge(Graph, E); } } //如果顶点有数据的话,读入数据 for (V = 0; V < Graph->Nv;V++) { //scanf("%c", &(Graph->Data[V])); } return Graph; } /* 邻接矩阵存储的图 - BFS */ /* IsEdge(Graph, V, W)检查<V, W>是否图Graph中的一条边,即W是否V的邻接点。 */ /* 此函数根据图的不同类型要做不同的实现,关键取决于对不存在的边的表示方法。*/ /* 例如对有权图, 如果不存在的边被初始化为INFINITY, 则函数实现如下: */ bool IsEdge(MGraph Graph, Vertex V, Vertex W) { return Graph->G[V][W] < INFINITY ? true : false; } void InitVisited() { for (int i = 0; i < MaxVertexNum;i++) { Visited[i] = false; } } void Visit(Vertex v) { printf("%d ", v); } //连通下的DFS和BFS void BFS(MGraph Graph, Vertex S, void(*Visit)(Vertex)) { /* 以S为出发点对邻接矩阵存储的图Graph进行BFS搜索 */ queue<Vertex> Q; Vertex V, W; Visit(S); Visited[S] = true; Q.push(S); while (!Q.empty()) { V = Q.front(); Q.pop(); for (W = 0; W < Graph->Nv; W++) /* 对图中的每个顶点W */ /* 若W是V的邻接点并且未访问过 */ if (!Visited[W] && IsEdge(Graph, V, W)) { /* 访问顶点W */ Visit(W); Visited[W] = true; Q.push(W); } } } void DFS(MGraph Graph, Vertex S, void(*Visit)(Vertex)) { /* 以V为出发点对邻接表存储的图Graph进行DFS搜索 */ Visited[S] = true; Visit(S); for (Vertex w = 0; w < Graph->Nv; w++) { if (IsEdge(Graph, S, w) && Visited[w] == false) { DFS(Graph, w, Visit); } } } //非连通下的遍历 Vertex listDFS(MGraph Graph, void(*Visit)(Vertex)) { Vertex i; for (i = 0; i < Graph->Nv; i++) { if (Visited[i] == false) break; } if (i == Graph->Nv) return 0; DFS(Graph, i, Visit); printf("\n"); return listDFS(Graph, Visit); } void DFSListComponents(MGraph Graph, void(*Visit)(Vertex)) { for (Vertex i = 0; i < Graph->Nv; i++) { if (Visited[i] == false) { DFS(Graph, i, Visit); printf("\n"); } } } void BFSListComponents(MGraph Graph, void(*Visit)(Vertex)) { for (Vertex i = 0; i < Graph->Nv; i++) { if (Visited[i] == false) { BFS(Graph, i, Visit); printf("\n"); } } } int main() { MGraph graph; graph = BuildGraph(); InitVisited(); listDFS(graph, &Visit); InitVisited(); DFSListComponents(graph, &Visit); InitVisited(); //BFS(graph,0,&Visit); BFSListComponents(graph, &Visit); return 0; }
结果:
Reference
数据结构学习笔记05图 (邻接矩阵 邻接表-->BFS DFS、最短路径)相关文章推荐
- (带权有向图)邻接矩阵表示图代码实现
- DOO-SABIN 细分正方体(1)利用邻接矩阵表示
- 图的邻接矩阵表示与最短路径算法( Dijkstra )代码实现
- 数据结构之图(Graph)的邻接矩阵表示的源代码
- 图的建立(邻接矩阵表示)
- 详解数据结构——图之邻接矩阵表示法
- 图(2)—— 邻接矩阵表示法
- 数据结构之---C语言实现图的数组(邻接矩阵)存储表示
- 图的邻接矩阵表示法及顶点入度、出度的计算方法
- 【数据结构】邻接矩阵表示法的图的深度广度优先遍历递归和非递归遍历
- 图的邻接矩阵表示广度深度遍历大连理工大学数据结构上机
- 有向图的邻接矩阵表示法(创建,DFS,BFS)
- 无向图有向图邻接矩阵表示法
- 图的深度优先遍历DFS(邻接矩阵表示法)
- 数据结构——图的数组实现(邻接矩阵表示法)
- 图的邻接矩阵表示(完整版)
- 邻接矩阵表示的深度优先搜索
- 图的邻接矩阵表示法
- Java编程实现邻接矩阵表示稠密图代码示例
- 图的邻接矩阵存储表示 深度优先和广度优先遍历