0
点赞
收藏
分享

微信扫一扫

【4月第一周学习记录】数据结构与算法王卓-第六章图-图的存储结构

目录

1. 图的逻辑结构

2. 图的表示方法

2.1 邻接矩阵(数组)表示法 (AMG — Adjacency Matrix Graph)

无向图

有向图 

创建无向网的邻接矩阵AMG的算法(伪代码)

邻接矩阵表示法的优缺点 

2.2 邻接表(链表)表示法 (ALG — Adjacency List Graph)

无向图的邻接表

有向图的邻接表

创建无向网的邻接表ALG的算法(伪代码)

邻接表表示法的优缺点 

邻接矩阵和邻接表的联系

2.3 补充:邻接表的改进存储结构—十字链表与邻接多重表

有向图:十字链表

无向图:邻接多重表


1. 图的逻辑结构

图的逻辑结构是多对多的关系。图没有顺序存储结构,但仍然可以用二维数组表示元素之间的关系

这就是邻接矩阵(数组)表示法

思考

如果使用链式存储结构,一个结点需要多少个指针域呢?

由于不好确定,图很难用多重链表的形式表现。一般使用邻接表,邻接多重表,十字链表来表示。

2. 图的表示方法

下面介绍邻接矩阵表示法和邻接表表示法。

2.1 邻接矩阵(数组)表示法 (AMG — Adjacency Matrix Graph)

无向图

  • 存储顶点的叫顶点表,是一维数组Vexs[n]。(顶点英文为Vertex
  • 存储边(顶点之间的关系)的是邻接矩阵,是一个二维数组arcs[n][n]。(弧英文为arc
  • 邻接矩阵中,如果两个顶点间存在关系,则记作1,否则为0(只考虑直接连线,非路径)

无向图的邻接矩阵的特点:

1. 主对角线上都是0。因为自己对自己没有邻接关系。

2. 矩阵关于主对角线对称。因为某两点之间的关系是相互的。(有没有联想到对角矩阵(带状矩阵),如三对角,五对角与七对角及压缩存储哈哈

3. 任一顶点的度为该行1的个数。

4. 完全图的邻接矩阵除了主对角线为0都是1。

例子:

有向图 

有向图的邻接矩阵的特点:

1. 主对角线上都是0,并不一定对称

2. 图中有多少条边,矩阵中就有多少个1,也可以逆推。

3. 某一结点 Vi 的度= 第 i 行数字和+第 i 列数字和;某一结点的出度=该横行的数字和;某一结点的入度=该纵列的数字和。因为横着看,每一行是出度边,描述该顶点能通往哪里。竖着看,每一列是入度边,描述哪些顶点能来到这里。

与之前不同的是,网中的每一条边都带有权。所以我们定义结点间关系不再用0和1表示,改为:

即两结点间如果有关系,则定义为权值而不是1;没关系,就定义为无穷大而不是0。

例:

创建无向网的邻接矩阵AMG的算法(伪代码)

定义部分

第一行定义是针对网的,因为没有关系时记为无穷,无法直接定义,所以用一个很大的数代替。

结构体内共四个元素,一个一维数组(顶点表),一个二维数组(邻接矩阵),两个int整形元素记录顶点数和边数。

初始化

思路(以无向网为例可以推广)

*如果要创建不是网,是图(即边没有权值),则初始化的时候不置无穷改为置0;构造邻接矩阵时不置为权改为置1;

**如果要创建的是有向网,则邻接矩阵中不加对称的那一条(更省事了)就可以了

算法部分

注意:G不是一个指针,定义时不是*AMGraph。所以用引用成员,如G.vexs, G.arcs。(当然,在vs编译器里会自动改)

实现思路第1~3步(输入顶点数和总边数两个int,输入顶点的信息,初始化邻接矩阵为极大值):

实现思路第4步:构造邻接矩阵

注意:无向网具有对称性,记得要改对应的。

补充第4步:根据输入的顶点获取数组下标的算法Locate:

思路为用for i++ =0遍历数组,if相等则返回i

邻接矩阵表示法的优缺点 

优点

注意:无向图直接看一横行就是顶点的度,有向图的话还要记得加上纵列(出度)。

缺点

  • 不利于增加删除顶点:传统的数组结构经典的缺点,一旦确定大小就不能改变了。
  • 空间复杂度:空间复杂度O(n²),跟边数无关(边多合适,但边少就浪费很多空间)
  • 遍历时间复杂度:统计总边数的时间复杂度为O(n²),即必须遍历邻接矩阵内所有元素

2.2 邻接表(链表)表示法 (ALG — Adjacency List Graph)

  • 存储顶点的仍然叫顶点表,是一维数组Vexs[n]。
  • 存储边(顶点之间的关系)的是一个线性链表arcs,如果是有向图则是记录该点发出的弧。
  • 表结点有2/3个域。第一个记邻接顶点在数组中的位置,如V1连接V2,V2是A[1]则为1,第二是指针域指向下一个结点,如果为网,有权值,则加第三个域info存放。

无向图的邻接表

特点:

  1. 邻接表不唯一 (联想,邻接矩阵是唯一的):以图中V1,表中vexs[0]为例,与V4,V2相连,则链表中1,3/3,1均可
  2. 无向图的邻接表的存储空间:O(n+2e),与O(n²)区别主要在边数上,利于存储稀疏图
  3. 邻接表中结点的度:就是链表中的结点数量

有向图的邻接表

根据用出度多还是入度多,选择使用邻接表还是逆邻接表

例:

创建无向网的邻接表ALG的算法(伪代码)

定义部分

顶点表Vexs

记录头结点,是邻接表的表头

VerTexType取决于信息的类型,如果是简单的char,int就可以直接用。

ArcNode是指向边结点的,边结点在链表(邻接表)中。

其中,定义最后的AdjList,即说明部分可能不太好理解。AdjList可以看成是一个数组名:执行AdjList v,v不是一个常量,而是一个包含许多VNode的数组。 

边结点表(邻接表)Arcs

三个组成部分:int记录指向的顶点(头结点),next指针,info记录sth like权

图的定义

共两项:一个头指针表,加两个int记录顶点数和弧数(都是附加信息视为一项)

PS:和邻接矩阵图AMG定义相比,之所以没有边的定义,是因为顶点就是边的头结点,只要知道头结点就能找到边。所以相当于两者一并包含在这个AdjList里了。加上顶点数和弧数,刚好也是三项:顶点表,边表,两个int(都是附加信息视为一项)。

例:常用操作

算法部分

思路

伪代码

*Locate函数不赘述,如果忘记烦请见上文构建无向网的代码部分

第三行稍微有点难理解,其实是用头插法。第一次执行是将p1.next=NULL, 然后插入,成为链表中除头结点外第一个也是最后一个结点;第二次执行p1.next就不是NULL了,而是上次的p1, 不懂的回去复习头插法,我把链接在下面贴出来(笑哭)

传送门:

【2月第四周学习记录】数据结构与算法王卓-第二章线性表-单链表(函数定义篇)_Finale_R的博客-CSDN博客

**见2-10 使用头插法创建链表

邻接表表示法的优缺点 

优点

  • 节约空间。对于n个结点的有向图:只需要n+e个结点;无向图则需要n+2e个结点。
  • 在无向图中计算顶点的度很方便。
  • 在无向图中方便找某一顶点的所有邻接点。

缺点

  • 无向图中,增加/删除一条边要操作两次(不同的头结点处)这也是引出邻接多重表的原因
  • 有向图中计算顶点的度比较麻烦。
  • 找两顶点间是否有边比较麻烦。
  • ...

邻接矩阵和邻接表的联系

相似

  • 同一张图/网的邻接矩阵中有几个1或权,邻接表中就有几个结点

不同 

  • 唯一性不同。同一张图/网的邻接矩阵只有一个,邻接表可以有很多个(arcs链表中结点顺序可以不同)
  • 空间复杂度不同。邻接矩阵空间复杂度固定为O(n²),而邻接表的空间复杂度固定为为O(n+e)(有向图的空间复杂度为O(n+e),无向图的空间复杂度为O(n+2e),但2可以约掉)
  • 适用性不同。邻接矩阵更适合存储稠密图,邻接表更适合存储稀疏图。

2.3 补充:邻接表的改进存储结构—十字链表与邻接多重表

来历

为了解决邻接表目前的缺点,引入了新的方法改进邻接表进行存储。

有向图:十字链表

概念

翻译:将出度入度结合

具体操作

顶点结点域从两个变为三个:增加一个入度域记录firstin第一个入度边

弧结点从两个变为四个:从a到b(两个)+下一个从a出发+下一个到b结束

例:

无向图:邻接多重表

为了解决加/减一条边要存/删两次的问题,将一条边同时关联到两个顶点。但相应的边结点的指针域就增加了。其中info可以存放权值等附加信息(对于网);mark域起到标记的效果,老师没有细讲,感兴趣可以自行深入探索。 

 

举报

相关推荐

0 条评论