• 【数据结构与算法】图的存储


    🔥 本文由 程序喵正在路上 原创,CSDN首发!
    💖 系列专栏:数据结构与算法
    🌠 首发时间:2022年11月13日
    🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
    🌟 一以贯之的努力 不得懈怠的人生

    邻接矩阵法(顺序存储)

    图中点和点之间是否有连接,我们可以用一个表来表示,其中 “1” 表示两点之间有边,反之

    在这里插入图片描述

    上图很容易用代码表示出来

    #define MaxVertexNum 100						//顶点数目的最大值
    typedef struct{
    	char Vex[MaxVertexNum];						//顶点表
    	int Edge[MaxVertexNum][MaxVertexNum];		//邻接矩阵,边表
    	int vexnum, arcnum;							//图的当前顶点数和边数/弧数
    } MGraph;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    顶点的数组中我们可以存储更复杂的信息,比如一个结构体

    结点数为 n n n 的图 G = ( V , E ) G = (V, E) G=(V,E) 的邻接矩阵 A A A n × n n \times n n×n 的,将 G G G 的顶点编号为 v 1 , v 2 , … , v n v_1, v_2, \dots, v_n v1,v2,,vn, 则

    KaTeX parse error: No such environment: align* at position 8: \begin{̲a̲l̲i̲g̲n̲*̲}̲ \begin{split} …

    在这种情况下,我们怎么求顶点的度、入度和出度呢?

    • 对于无向图,第 i i i 个结点的度 = = = i i i 行(或第 i i i 列)的非零元素个数
    • 对于有向图,第 i i i 个结点的出度 = = = i i i 行的非零元素个数,第 i i i 个结点的入度 = = = i i i 列的非零元素个数,,第 i i i 个结点的度 = = = i i i 行、第 i i i 列的非零元素个数之和

    邻接矩阵法求顶点的度 / 出度 / 入度的时间复杂度均为 O ( ∣ V ∣ ) O(|V|) O(V)

    邻接矩阵法存储带权图(网)

    #define MaxVertexNum 100						//顶点数目的最大值
    #define INFINITY 最大的int//宏定义常量 “无穷”
    typedef char VertexType;						//顶点的数据类型
    typedef int EdgeType;							//带权图中边上权值的数据类型
    typedef struct{
    	VertexType Vex[MaxVertexNum];				//顶点
    	EdgeType Edge[MaxVertexNum][MaxVertexNum];	//边的权
    	int vexnum, arcnum;							//图的当前顶点数和弧数
    } MGraph;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    邻接矩阵法的性能分析

    空间复杂度 O ( ∣ V ∣ ) O(|V|) O(V) —— 只和顶点数相关,和实际的边数无关,邻接矩阵法采用数组顺序存储的形式,适合用于存储稠密图

    无向图的邻接矩阵是对称矩阵,我们可以压缩存储,也就是只存储上三角区或者下三角区

    邻接矩阵法的性质

    在这里插入图片描述

    设图 G G G 的邻接矩阵为 A A A(矩阵元素为 0 / 1 0/1 0/1),则 A n A^n An 的元素 A n [ i ] [ j ] A^n[i][j] An[i][j] 等于由顶点 i i i 到顶点 j j j 的长度为 n n n 的路径的数目

    假设 A A A 如下表所示

    在这里插入图片描述

    现在要计算 A 2 [ 1 ] [ 4 ] , A 2 [ 2 ] [ 2 ] A^2[1][4], A^2[2][2] A2[1][4],A2[2][2],该怎么做呢?
    A 2 [ 1 ] [ 4 ] = a 11 × a 14 + a 12 × a 24 + a 13 × a 34 + a 14 × a 44 = 1 A 2 [ 2 ] [ 2 ] = a 21 × a 12 + a 22 × a 22 + a 23 × a 32 + a 24 × a 42 = 3 A^2[1][4] = a_{11} \times a_{14} + a_{12} \times a_{24} + a_{13} \times a_{34} + a_{14} \times a_{44} = 1 \\ A^2[2][2] = a_{21} \times a_{12} + a_{22} \times a_{22} + a_{23} \times a_{32} + a_{24} \times a_{42} = 3 A2[1][4]=a11×a14+a12×a24+a13×a34+a14×a44=1A2[2][2]=a21×a12+a22×a22+a23×a32+a24×a42=3
    其中 a 11 a_{11} a11 表示矩阵的第 1 1 1 行第 1 1 1 个元素,其他依此类推

    我们画出 A 2 A^2 A2 的表,如下

    在这里插入图片描述

    可以发现,我们计算出来的值就是这个表中对应位置的值

    如果让你计算 A 3 [ 1 ] [ 4 ] A^3[1][4] A3[1][4] 呢?

    邻接表法(顺序+链式存储)

    在这里插入图片描述

    // “边/弧”
    typedef struct ArcNode{
    	int adjvex;						//边/弧指向哪个结点
    	struct ArcNode *next;			//指向下一条弧的指针
    	InfoType info;					//边权值(没有可不加)
    }ArcNode;
    
    // “顶点”
    typedef struct VNode{
    	VertexType data;			//顶点信息
    	ArcNode *first;				//第一条边/弧
    }VNode, AdjList[MaxVertexNum];
    
    //用邻接表存储的图
    typedef struct{
    	AdjList vertices;
    	int vexnum, arcnum;
    }ALGraph;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    当然,有向图也可以用邻接表法来表示

    在这里插入图片描述

    空间复杂度分析

    对于无向图,由于每一条边都会有一个指向的结点占空间,而且是双向的,比如 A A A B B B 之间有一条边,那么
    A A A B B B 这条边就会有一个指向 B B B 的结点,同理 B B B A A A 这条边就会有一个指向 A A A 的结点,由于边结点的数量是 2 ∣ E ∣ 2|E| 2E ,所以整体的空间复杂度为 O ( ∣ V ∣ + 2 ∣ E ∣ ) O(|V| + 2|E|) O(V+2E)

    对于有向图,由于每条边都是有方向的,所以只会有一个指向结点,整体的空间复杂度即为 O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)

    度、入度和出度

    那么,下面我们思考一下怎么求顶点的度、入度和出度呢?

    • 对于无向图,我们只要遍历边结点的链表就能求出对应顶点的度
    • 对于有向图,通过遍历边结点的链表我们就能求出对应顶点的出度;但是要得到某个顶点的入度,就很麻烦了,我们需要遍历所有顶点相应的边结点的链表,这也是邻接表法的一个缺点

    邻接矩阵和邻接表的对比

    图的邻接表表示方式并不唯一,与一个顶点有关系的边的顺序是可以颠倒排列的;但是对于前面的邻接矩阵来说,只要确定了顶点编号,那图的邻接矩阵就是唯一的

    对比角度邻接矩阵邻接表
    空间复杂度 O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)无向图 O ( ∣ V ∣ + 2 ∣ E ∣ ) O(|V| + 2|E|) O(V+2E);有向图 O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)
    适合用于存储稠密图存储稀疏图
    表示方式唯一不唯一
    计算度 / 出度 / 入度必须遍历对应行或列计算有向图的度、入度不方便,其余很方便
    找相邻的边必须遍历对应行或列找有向图的入边不方便,其余很方便

    十字链表

    我们需要定义两个结构:弧结点和顶点结点

    在这里插入图片描述

    空间复杂度为: O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)

    如何找到指定顶点的所有出边? —— 顺着绿色线路找
    如何找到指定顶点的所有入边? —— 顺着橙色线路找

    注意:十字链表只用于存储有向图

    邻接多重表

    在这里插入图片描述

    空间复杂度为: O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)

    删除边、删除节点等操作很方便

    注意:邻接多重表只用于存储无向图

    总结

    角度邻接矩阵邻接表十字链表邻接多重表
    空间复杂度 O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)无向图 O ( ∣ V ∣ + 2 ∣ E ∣ ) O(|V| + 2|E|) O(V+2E);有向图 O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E) O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E) O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)
    找相邻边遍历对应行或列,时间复杂度为 O ( ∣ V ∣ ) O(|V|) O(V)找有向图的入边必须遍历整个邻接表很方便很方便
    删除边或顶点删除边很方便,删除顶点需要移动大量数据无向图中删除边或顶点都不方便很方便很方便
    适用于存储稠密图存储稀疏图或其他只能存储有向图只能存储无向图
    表示方式唯一不唯一不唯一不唯一
  • 相关阅读:
    Go 语言
    面试经历一
    福建三明大型工程机械3D扫描工程零件三维建模逆向抄数-CASAIM中科广电
    Facebook视角下的文化多样性:全球社交的聚合
    Linux CentOS7 添加中文输入法
    Apache DolphinScheduler新一代分布式工作流任务调度平台实战
    我用代码帮亲戚处理了评职称的材料问题,顿时觉得写代码还是挺香的
    基于布谷鸟优化K均值的WSN分簇路由算法
    redis(分布式篇)
    【算法集训专题攻克篇】第十四篇之栈
  • 原文地址:https://blog.csdn.net/weixin_62511863/article/details/127761281