数据结构 —— 图

树是一种非线性表的数据结构,图也是,图和树比起来,是一种更加复杂的非线性表数据结构。

涉及图的算法有很多,也非常复杂,比如图的搜索、最短路径、最小生成树、二分图等等。

树中的元素称为节点,而图中的元素称为顶点。

图中的一个顶点可以与任意其他顶点建立连接关系。

这种建立的关系叫作边(edge)。

微博、微信等这些社交网络的好友关系就是一个非常典型的图结构。

如果两个用户之间互加好友,那就在两者之间建立一条边。所以,整个微信的好友关系就可以用一张图来表示。

其中,每个用户有多少个好友就对应到跟顶点相连接的边的条数,就叫做顶点的度(degree)。

有向图和无向图

实际上,有些社交软件不同,比如微博。微博允许单向关注,也就是说,用户 A 关注了用户 B,但用户 B 可以不关注用户 A。

用图也可以表示这种单向的社交关系。其实边是可以有方向的

如果用户 A 关注了用户 B,我们就在图中画一条从 A 到 B 的带箭头的边,来表示边的方向。

如果用户 A 和用户 B 互相关注了,那我们就画一条从 A 指向 B 的边,再画一条从 B 指向 A 的边。

我们把这种边有方向的图叫作“有向图”。把边没有方向的图就叫作“无向图”。

在有向图中,我们把度分为入度(In-degree)和出度(Out-degree)。

顶点的入度,表示有多少条边指向这个顶点;

顶点的出度,表示有多少条边是以这个顶点为起点指向其他顶点。

就像是微博,入度就表示有多少粉丝,出度就表示关注了多少人。

带权图

还有一种社交软件:QQ,QQ有一个亲密度的功能。

QQ 不仅记录了用户之间的好友关系,还记录了两个用户之间的亲密度。

如果两个用户经常往来,那亲密度就比较高;如果不经常往来,亲密度就比较低。

要想记录图中记录这种好友关系的亲密度,就要用到带权图带权图(weighted graph)。

在带权图中,每条边都有一个权重(weight),我们可以通过这个权重来表示 QQ 好友间的亲密度。

图的储存方法

 

 邻接矩阵(Adjacency Matrix)

邻接矩阵是图最直观的一种存储方法。

邻接矩阵的底层依赖一个二维数组。

对于无向图来说,如果顶点 i 与顶点 j 之间有边,我们就将 A[i][j]和 A[j][i]标记为 1;

对于有向图来说,如果顶点 i 到顶点 j 之间,有一条箭头从顶点 i 指向顶点 j 的边,那我们就将 A[i][j]标记为 1。

对于带权图,数组中就存储相应的权重。

缺点

用邻接矩阵来表示一个图,虽然简单、直观,但是比较浪费存储空间。

对于无向图来说,如果 A[i][j]等于 1,那 A[j][i]也肯定等于 1。实际上,我们只需要存储一个就可以了。

也就是说,无向图的二维数组中,如果我们将其用对角线划分为上下两部分。

那我们只需要利用上面或者下面这样一半的空间就足够了,另外一半白白浪费掉了。

另外如果,如果我们存储的是稀疏图(Sparse Matrix),也就是说,顶点很多,但每个顶点的边并不多。

那邻接矩阵的存储方法就更加浪费空间了。

比如微信有好几亿的用户,对应到图上就是好几亿的顶点。但是每个用户的好友并不会很多,一般也就三五百个而已。

如果我们用邻接矩阵来存储,那绝大部分的存储空间都被浪费了。

优点

邻接矩阵的存储方式简单、直接,因为基于数组,所以在获取两个顶点的关系时,就非常高效。

用邻接矩阵存储图的另外一个好处是方便计算。

这是因为,用邻接矩阵的方式存储图,可以将很多图的运算转换成矩阵之间的运算。

邻接表(Adjacency List)

邻接表有点像散列表,每个顶点对应一条链表,链表中存储的是与这个顶点相连接的其他顶点。

图中是一个有向图 的邻接表存储方式,每个顶点对应的链表里面,存储的是指向的顶点。

对于无向图来说也是类似的,不过,每个顶点的链表中存储的是跟这个顶点有边相连的顶点。

如果我们要确定,是否存在一条从顶点 2 到顶点 4 的边,那我们就要遍历顶点 2 对应的那条链表,看链表中是否存在顶点 4。

比起邻接矩阵的存储方式,在邻接表中查询两个顶点之间的关系就没那么高效了。

邻接矩阵存储起来比较浪费空间,但是使用起来比较节省时间。

相反,邻接表存储起来比较节省空间,但是使用起来就比较耗时间。

邻接表长得很像散列表,在基于链表法解决冲突的散列表中,如果链过长的话。

为了提高查找效率,我们可以将链表换成其他更加高效的数据结构,比如平衡二叉查找树等。

我们也可以将邻接表同散列表一样进行“改进升级”。我们可以将邻接表中的链表改成平衡二叉查找树。比如红黑树

我们就可以更加快速地查找两个顶点之间是否存在边了。

二叉查找树可以换成其他动态数据结构,比如跳表、散列表等。

除此之外,我们还可以将链表改成有序动态数组,可以通过二分查找的方法来快速定位两个顶点之间否是存在边。

发布了113 篇原创文章 · 获赞 25 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_42006733/article/details/104732888