zoukankan      html  css  js  c++  java
  • 图的邻接矩阵表示

    图的邻接矩阵表示

    图基概念(Graph)

    • 包含

      • 一组顶点:通常用V (Vertex) 表示顶点集合
      • 一组边:通常用E (Edge) 表示边的集合
      • 边是顶点对:(v, w) ∈E ,其中v, w ∈ V有向边<v, w> 表示从v指向w的边(单行线)不考虑重边和自回路
    • 无向图:边是无向边(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][N]——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("
    ");
    
    	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("
    ");
    		}
    	}
    }
    void BFSListComponents(MGraph Graph, void(*Visit)(Vertex))
    {
    	for (Vertex i = 0; i < Graph->Nv; i++) {
    		if (Visited[i] == false) {
    			BFS(Graph, i, Visit);
    			printf("
    ");
    		}
    	}
    }
    
    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、最短路径)

  • 相关阅读:
    pku2226 Muddy Fields
    pku3715 Blue and Red
    关于二分图的最大权匹配
    pku 2262&& pku 2739 && pku 3006
    pku2060 Taxi Cab Scheme
    pku 1486 Sorting Slides
    id、css命名规范
    Git 常用命令
    sublime text3插件使用
    Java实现数据结构栈stack和队列Queue
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/6693121.html
Copyright © 2011-2022 走看看