图的存储结构

你说的曾经没有我的故事 提交于 2020-01-10 00:34:35

图的存储结构

这里我们只要介绍五种存储结构的理论和特点以及邻接矩阵,邻接表,边集数组的c语言表示,十字链表和邻接多重表是图的遍历可视化系统要用的存储结构在下一节我们将针对这两中数据结构进行详细讲解包括这两种结构对于图的各种操作(用java描述)

图的存储结构相对于线性表和树来说相对要复杂得多,我们所说的“顶点的位置”或“邻接点的位置“只是一个相对的概念。从图的逻辑定义结构来看,图上任何一个顶点都可以被看成是第一个顶点,任一顶点的邻接点之间不存在次序关系,比如如下四张图:
在这里插入图片描述
其实他们是同一个图

正是由于图的复杂性,任意两个顶点之间存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系,也就是说,图不可能用简单的顺序结构来表示。而多重链表的方式,即一个数据域和多个指针组成的结点表示图中的一个顶点,尽管这样可以实现图结构,但这样做是有问题的。如果各个顶点之间的度相差很大,按度最大的顶点设计结点结构会造成很多存储单元的浪费,而若按每个顶点自己的度数设计顶点结构,在操作上也会带来很多不便。所以为了解决此难题,前辈们提出了五种存储结构

1.邻接矩阵

定义:图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中的顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或者弧的信息
对于一个无权图来说:
设图有G个顶点,则邻接矩阵是一个n*n的方阵,定义为:
在这里插入图片描述
如下图就是一个无向图和它的邻接矩阵
在这里插入图片描述
从上图可以看出对于一个无向,无权图而言我们用0表示无边,1表示有边,而无向图的正因为其无向的特点所以一个无向图的所有边只需要看邻接矩阵的上三角矩阵或者下三角矩阵就可以了,也就是说无向图的邻接矩阵是一个对称矩阵、
下图是一个有向图和它的邻接矩阵
在这里插入图片描述
有向图的邻接矩阵其每一行代表对应顶点的出度,每一列代表对应顶点的入度

如果是网的话:
设G是网图,有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:
在这里插入图片描述
下图是一个·有向网:
在这里插入图片描述
我们用无穷大表示顶点之间没有边,0表示自身之间没有连接,其他数字表示依附于顶点的权值

邻接矩阵的数据结构:
抽象数据类型(ADT)
在这里插入图片描述
构造邻接矩阵:
在这里插入图片描述
在这里插入图片描述
时间·复杂度:O(n+n^2+e)其中初始化矩阵的时间复杂度为O(n2)

2.邻接表

邻接矩阵是一个非常不错的数据结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间极大的浪费的
如下稀疏图:
在这里插入图片描述
图中只有v1–>v0一条弧极大的浪费了存储空间

因此,我们需要考虑另一种存储结构,在顺序存储结构中我们也出现预先分配内存造成存储空间浪费的问题,于是引出了链式存储结构。同样我们也可以考虑对边或者弧使用链式存储的方式来避免空间浪费的问题。

定义:我们把数组与链表结合相结合的存储方法称之为邻接表(Adjacency List)
邻接表的处理办法是这样的:
1.图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以比较容易的读取顶点信息,更加方便。另外,对于顶点数组中,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息

2.图中每一个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点vi的边表,有向图称为顶点vi的出边表

下图是一个无向图的邻接表结构
在这里插入图片描述
1.data为顶点数据域
2.firstedge为顶点连接的第一条边
3.adjvex为与顶点连接顶点的下标
4.next为与相同顶点连接的下一条边

但是对于有向图由于弧是有方向的,我们是以顶点为弧尾来存储边表的,这样很容易就得到一个顶点的出度,但也有时为了便于确定顶点的入度或者以顶点为弧头的弧,我们可以建立一个有向图的逆邻接表,即对每个顶点vi都建立一个链接为vi为弧头的表

在这里插入图片描述
此时我们很容易就可以算出来某个顶点的出度和入度是多少,判断两顶点之间是否存在弧也很容易实现。

对于带权值的图来说我们给边表结点加一个weight数据域即可
在这里插入图片描述
邻接表的抽象数据类型(ADT)
在这里插入图片描述
用邻接表构造图
在这里插入图片描述
在这里插入图片描述
这里加粗的代码使用了·头插法,由于对于无向图,一条边对应都是两个顶点,所以在循环中,一次性对i和·j都进行了插入,对于n个顶点e条边来说,很容易得出是O(n+e)

3.十字链表

对于有向图而言,邻接表是有缺陷的。关心了出度问题,想了解入度就必须要遍历整个图才能知道,反之,逆邻接表解决了入度却不了解出度的情况。那么把邻接表和逆邻接表结合起来就是解决这个问题的方法,这种结构我,我们称之为十字链表
顶点结构如下
在这里插入图片描述
边表结点如下
在这里插入图片描述
1.data为顶点数据域
2.firstin为以当前顶点为弧头的第一条边
3.firstout为以当前结点为弧尾的第一条边
4.tailvex为当前边的弧尾
5.headvex为当前边的弧头
6.headlink连接以headvex为弧头的边
7.taillink连接以tailvex为弧尾的边
以下是一个有向图的十字链表
在这里插入图片描述
数据结构:
再下一节详细说明。。。。

4.邻接多重表

对于无向图而言如果是针对顶点的操作邻接表固然是一个不错的选择,但是针对边的操作就显得极为不方便
如下图
在这里插入图片描述
如果要删除(v0,v2)这条边,需要对邻接表结构中右边表阴影两个节点进行删除操作,显然是比较繁琐的
因此我们也仿照十字链表的方式,对边表结点的结构进行一些改造,也许就可以避免刚才的问题

重新定义边表结点结构如下:
在这里插入图片描述
ivex和jvex是与某条边依附的两个顶点的下标。ilink指向依附顶点ivex的下一条边,jlink指向依附顶点jvex的下一条边,这就是邻接多重表结构

如下图·:
在这里插入图片描述
我们为了绘图方便,都把ivex的下标设置的和顶点下标相同,然后开始连线:
在这里插入图片描述
数据结构:
下一节详解。。。。。

5.边集数组

边集数组有两个一维数组构成,一个存储顶点信息,另一个存储边信息,这个边数组每一个数据元素由一条边的起点下标(begin),终点下标(end)和权值(weight)组成,这个结构用处有限(在布鲁斯卡尔算法里可以使用因为更关注边的操作)我们只讲讲:
在这里插入图片描述
边数组元素
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!