zoukankan      html  css  js  c++  java
  • 数据结构:图及其邻接矩阵与邻接表表示

    六度空间理论

    图中两个顶点若要联系,最多通过6个结点便可以完成 。

    基本概念

    • 图用于表示“多对多”的关系。

    • 包含

    • 一组顶点:通常用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的最小生成树;

    怎么在程序中表示一个图

    • 对顶点进行编号。
    • 顶点序号所对应在邻接矩阵中的值为1。
    • 因为没有自回路,所以对角线全为0。
    • 无向图为对称矩阵。

    问题:对于无向图的存储,怎样可以省一半空间?

    • 用一个长度为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元素的
      个数是“入度”

    邻接矩阵—— 有什么不好?

    • 浪费空间—— 存稀疏图(点很多而边很少)有大量无效元素
    • 对稠密图(特别是完全图)还是很合算的
    • 浪费时间—— 统计稀疏图中一共有多少条边

    邻接表

    • G[N]为指针数组,对应矩阵每行一个链表,只存非0元素

    邻接矩阵—— 有什么好处?

    • 方便找任一顶点的所有“邻接点”
    • 节约稀疏图的空间:需要N个头指针+2E(每个节点至少两个域)
    • 方便计算任一顶点的“度”?:对无向图:是的,对又想吐只能计算出度,需要构造“逆邻接表”计算入度。

    邻接矩阵—— 有什么不好?

    • 不方便检查任意一对顶点间是否存在边。

    代码实现

    • 设定初始参数,最大值和权值顶点数
    • 设定边结点,用于连接顶点。
    • 设定图结点,建立顶点数,边数,表示关系的邻接矩阵。
    • 设定插入函数,把边插入到图当中。

    设定参数

    • 设定INFINITY为最大值,便于权值的比较。
    #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; /* 以邻接矩阵存储的图类型 */
     
    

    创建一个图

    • 创建一个图结点并将顶点初始化,返回指针。
    
     
    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;
    }
    
  • 相关阅读:
    linux常用命令
    10.8统计英文词频
    9月10号作业
    华氏温度与摄氏温度转换
    小故事
    Java的工厂模式(三)
    Javascript实现图片翻转
    Java的工厂模式(二)
    Java的工厂模式(一)
    Java新建线程的两种方式
  • 原文地址:https://www.cnblogs.com/vancasola/p/8039194.html
Copyright © 2011-2022 走看看