• DSA之图(2):图的存储结构


    0 图的结构

    多对多的结构,不能跟以前一样确定多少个数据域和指针域来进行表示。主要介绍邻接矩阵和邻接表。


    在这里插入图片描述

    1 邻接矩阵

    在这里插入图片描述
    尖括号表示有序的,有向图;圆括号表示无序的,无向图。有边的记作1,没有边的记为0。

    1.1 无向图的邻接矩阵

    在这里插入图片描述
    特点:

    1. 对角线都是0,因为自身对自身没有边
    2. 无向图的邻接矩阵是对称的,两个顶点的边是相互的;但是并不能说有向图的邻接矩阵一定是不对称
    3. 度是与顶点相关联的边的个数,反映到图上来说,就是找1的个数,即:顶点 i i i的度 = 第 i i i行(列)中1的个数,即行(列)元素之和
    4. 完全图的邻接矩阵,对角线的元素为0,其余为1

    1.2 有向图的邻接矩阵

    有向图的邻接矩阵不一定是对称的,因为两个相连的顶点可能不是双向的。
    在这里插入图片描述

    1. i i i行,表示从 v i v_i vi个节点发出的弧,称之为出度。
    2. i i i列,表示从 v i v_i vi个节点接收的弧,称之为入度。
    3. 顶点的出度=第 i i i行元素之和
    4. 顶点的入度=第 i i i列元素之和
    5. 顶点的度=第 i i i行元素之和+第 i i i列元素之和

    1.3 网(有权图)的邻接矩阵表示法

    在这里插入图片描述
    相当于就是把权值代替成了普通的边。
    在这里插入图片描述
    以上就是网的邻接矩阵。

    1.4 邻接矩阵的建立

    #define MaxInt 32767 //表示无穷大
    #define MVNum 100
    typedef char VerTexType;//设顶点的数据类型为字符型
    typedef int ArcType; //假设边的权值类型为整型
    
    typedef struct
    {
        VerTextType vex[MVNum];//顶点表
        ArcType arcs[MVNum][MVNum];//邻接矩阵
        int vexnum, arcnum; //图的当前点数和边数
    }AMGraph;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.4.1 采用邻接矩阵建立无向网

    在这里插入图片描述

    #define MaxInt 32767 //表示无穷大
    #define MVNum 100
    typedef char VerTexType;//设顶点的数据类型为字符型
    typedef int ArcType; //假设边的权值类型为整型
    
    typedef struct
    {
        VerTextType vex[MVNum];//顶点表
        ArcType arcs[MVNum][MVNum];//邻接矩阵
        int vexnum, arcnum; //图的当前点数和边数
    }AMGraph;//邻接矩阵表示的图
    
    int LocateVex(AMGraph G, VertexType u)//查找顶点所在的位置
    {
        int i;
        for (i = 0; i < G.vexnum; ++i)
        {
            if (u == G.vex[i])
            {
                return i;//找到元素在图当中的下标
            } 
        }
        return -1;
    }
    
    //构建无向网
    Status CreateUDN(AMGraph &G)//G有四个成员,顶点表,边表,点数和边数
    {
        cin >> G.vexnum >> G.arcnum;//输入总顶点数,总边数
        for (i = 0; i < G.vexnum; ++i)
        {
            cin >> G.vex[i];//依次输入点的信息
        }
        for (i = 0; i < G.vexnum; ++i) //初始化邻接矩阵
        {
            for (j = 0; i < G.vexnum; ++j)
            {
                G.arcs[i][j] = MaxInt;//边的权值均初始化为最大值
            }
        }
        for (k = 0; k < G.arcnum; ++k)//构造邻接矩阵
        {
            cin >> v1 >> v2 >> w;//输入一条边所依附的顶点以及边的权值
            i = LocateVex(G, v1);//确定v1和v2在G中的位置
            j = LocateVex(G, v2);
            G.arcs[i][j] = w;//边的权值置为w
            G.arcs[j][i] = G.arcs[i][j];
        }//for
        return OK;
    }//CreateUDN
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    1.4.2 采用邻接矩阵建立有向网

    在这里插入图片描述
    无需向邻接矩阵双向赋值,只需要赋值单边的就行。

    1.5 邻接矩阵的优缺点

    1.5.1 优点

    在这里插入图片描述

    1.5.2 缺点

    在这里插入图片描述
    时间复杂度只与顶点有关,与边数无关,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

    2 邻接表

    表示方法

    • 顶点:按编号顺序将顶点数据存储在一维数组中;
    • 关联同一顶点的边(以顶点为尾的弧):用线性链表存储

    在这里插入图片描述

    Tips:对于邻接表的表示方法,举个例子, v 1 v_1 v1之后的是3,这个3对应的是表中的下标,也就是 v 4 v_4 v4,不要直接认为3就是 v 3 v_3 v3

    2.1 无向图的邻接表

    在这里插入图片描述

    边得双倍。怎么计算度?看到有几个表节点(紫色的点),就是有几个与其关联的边。所以,无向图中顶点 v i v_i vi的度为第 i i i个单链表中的结点数。

    2.2 有向图的邻接表

    在这里插入图片描述
    特点:

    1. 顶点 v i v_i vi的出度为第 i i i个单链表中的结点个数。
    2. 顶点 v i v_i vi的入度为整个单链表中邻接点域值是 i − 1 i-1 i1的结点个数。

    综上,找出度容易,入度难。

    在这里插入图片描述
    还有一个逆邻接表表示,这样就是找入度容易,找出度难

    练习:已知某网的邻接(出边)表,请画出该网络
    在这里插入图片描述
    当邻接表的存储结构形成后,图便唯一确定。

    2.3 邻接表的建立

    在这里插入图片描述

    #define MVNum 100 //最大顶点数
    typedef struct VNode
    {
        VerTexType data; //顶点信息
        ArcNode * firstarc; //指向第一条依附该顶点的边的指针
    }VNode, AdjList[MVNum]; //AdjList为邻接表
    
    typedef struct ArcNode //边结点
    {
        int adjvex; //该边所指向的顶点的位置
        struct ArcNode * nextarc; //指向下一条边的指针
        OtherInfo info; //和边相关的信息
    }ArcNode;
    
    typedef struct //图结构的定义
    {
        AdjList vertices;//vertices -- vertex的复数
        int vexnum, arcnum; //图的当前顶点数与弧数
    }ALGraph;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    构建图G后,直接用成员变量进行赋值。

    2.3.1 采用邻接表建立无向网

    在这里插入图片描述

    #define MVNum 100 //最大顶点数
    typedef struct VNode
    {
        VerTexType data; //顶点信息
        ArcNode * firstarc; //指向第一条依附该顶点的边的指针
    }VNode, AdjList[MVNum]; //AdjList为邻接表
    
    typedef struct ArcNode //边结点
    {
        int adjvex; //该边所指向的顶点的位置
        struct ArcNode * nextarc; //指向下一条边的指针
        OtherInfo info; //和边相关的信息
    }ArcNode;
    
    typedef struct //图结构的定义
    {
        AdjList vertices;//vertices -- vertex的复数
        int vexnum, arcnum; //图的当前顶点数与弧数
    }ALGraph;
    
    //
    
    Status CreateUDG(ALGraph &G) //采用邻接表表示法,创建无向图G
    {
        cin >> G.vexnum >> G.arcnum; //输入总项点数,总边数
        for (i = 0; i < G.vexnum; ++i)//输入各点,构造表头结点表
        {
            cin >> G.vertices[i].data; //输入顶点值
            G.ertices[i].firstarc = NULL; //初始化表头结点的指针域
        }
        for (k = 0; k < G.arcnum; ++k) //输入各边,构造邻接表
        {
            cin >> v1 >> v2;    //输入一条边依附的两个顶点
            i = LocateVex(G, v1);
            j = LocateVex(G, v2);
            p1 = new ArcNode; //生成一个新的边结点*p1
            p1->adjvex = j; //邻接点序号为j
            p1->nextarc = G.vertices[i].firstarc;
            G.vertices[i].firstarc = p1;//将新结点*p1插入顶点vi的边表头部
            p2 = new ArcNode; //生成另一个对称的新的边结点*p2
            p2->adjvex = i; //邻接点序号为i
            p2->nextarc = G.vertices[j].firstarc;
            G.vertices[j].firstarc = p2; //将新节点*p2插入顶点vj的边表头部
        }
       return OK;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    2.3.2 采用邻接表建立有向网

    如果建立有向网,就只需要建立入度/出度的。

    2.4 邻接表的特点

    在这里插入图片描述

    • 无向图是 N + 2 E N+2E N+2E,有向图是 N + E N+E N+E
    • 无向图方便计算度,有向图方便计算出度,不方便入度,入度计算得靠逆邻接表。

    3 邻接矩阵与邻接表的区别

    3.1 联系

    在这里插入图片描述

    3.2 区别

    在这里插入图片描述

    邻接表因为节点插入顺序不同,导致不唯一。

    4 改进的邻接表

    在这里插入图片描述

    4.1 十字链表—用于有向图

    将邻接表与逆邻接表结合起来。
    在这里插入图片描述
    在这里插入图片描述
    给表头结点再增加一个指针域,用来放入度边(因为有向图的邻接表算出度好算)
    在这里插入图片描述
    顶点节点:

    1. firstout表明第一条出弧(出度边)
    2. firstin表明第一条入弧(入度边)

    弧节点:

    1. tailvex表明弧尾位置(出度时顶点是弧尾)
    2. headvex表明弧头位置(入度时顶点是弧头)
    3. hlink表明弧头相同的下一条弧(入度指向同一节点,找headvex相同的即可)
    4. tlink表明弧尾相同的下一条弧(出度从同一节点出发,找tailvex相同的即可)

    在这里插入图片描述
    以上就是十字链表,统计入度出度很方便。

    4.2 邻接多重表

    解决无向图每条边存储两遍的问题。
    在这里插入图片描述
    在邻接表当中,任意一条边,都会出现两次。
    现在换成邻接多重表后,就记录好这个边是哪两个顶点之间的边就行了。

    邻接多重表的结构,顶点节点与边结点的表示形式:
    在这里插入图片描述

    5 总结

    以上便是图的存储结构,如有遗漏,欢迎评论区补充,感谢。

  • 相关阅读:
    cap分布式理论
    golang的defer执行时机案例分析
    Predict Future Sales销量预测-Kaggle练习
    【笔记】期末的jsp复习之数据库
    企业网站的制作流程是什么?设计和制作一个网站需要多长时间?
    【单片机毕业设计选题24020】-全自动鱼缸的设计与应用
    Flink 本地任务添加配置参数
    【Saras算法】TD Learning的一种
    【云开发】小程序端操作数据库详解
    练习-Java输入输出之文件字节IO流之合并文件
  • 原文地址:https://blog.csdn.net/weixin_44673253/article/details/126086165