图的表示

1. 如何理解 “图”

图由顶点(vertex)和(edge)组成,顶点之间通过边来建立一种联系。

生活中有很多符合图结构的例子,比如社交网络,就是一个非常典型的图结构。在微信中,每个用户可以看作是一个顶点,如果两个用户互为好友,那就在这两个顶点之间建立一条边。一个用户的好友数量,也就是和这个顶点相连接的边的个数,称为这个顶点的(degree)。

在微博中,还允许单向关注,也就是说用户 A 关注了 B,但用户 B 可以不关注用户 A。对此,我们引入边的 “方向” 这个概念。用户 A 关注了用户 B,我们就用一条从 A 到 B 的带箭头的边来表示。反之,用户 B 关注了用户 A,我们就用一条从 B 到 A 的带箭头的边来表示。这种有方向的图称为 “有向图”,反之则称为 “无向图”。

在有向图中,我们将指向这个顶点的所有边的数量叫作入度(In-degree),以这个顶点为起点而指向其他顶点的所有边的数量叫作出度(Out-degree)。在微博中,入度就是粉丝数量,出度就是关注数量。

另外,在 QQ 中,两个用户之间的亲密度则是通过带权图(weighted graph)来表示的。所谓带权图,就是每条边都有一个权重,这个权重大小就可以作为两个用户之间的亲密度的度量。

2. 邻接矩阵存储方法

图最直观的一种存储方法,就是邻接矩阵(Adjacency Matrix)。

邻接矩阵其实就是一个二维数组。对于无向图,如果顶点 i 和顶点 j 之间有边,那么 Ai 和 Aj 就等于 1。对于有向图,如果边从顶点 i 指向顶点 j ,那么 Ai 就等于 1,如果边从顶点 j 指向顶点 i ,那么 Aj 就等于 1。而对于带权图,数组中存储的就是对应边的权重。

用邻接矩阵来表示图非常简单、直观,但是比较浪费内存空间。

对无向图来说, Ai 和 Aj 始终是相同的,邻接矩阵是一个对称矩阵,只需要一半空间就可以了。

另外,如果存储的是稀疏图(Sparse Graph),也就是顶点非常多,但每个顶点的边并不多,那邻接矩阵就更加浪费空间了。比如,微信有几亿用户,但每个用户的好友却不会很多。

邻接矩阵的优点就是简单、直接,因为是基于数组存储,因此获取两个顶点的关系就非常高效。而且,将图存储为邻接矩阵,还可以将很多图的运算转换为矩阵之间的运算,计算非常方便。

3. 邻接表存储方法

由于邻接矩阵比较浪费内存,我们提出了另一种图的存储方法——邻接表(Adjacency List)。

邻接表有点类似于散列表,每个顶点对应一个链表,链表中存储的是与这个顶点相连接的其它顶点。

邻接矩阵存储起来比较浪费空间,但是使用起来比较节省时间;邻接表虽然存储起来比较节省空间,但使用起来就会比较耗时。比如,我们要在邻接表中查找两个顶点之间是否有相连接的边,就要去其中一个顶点对应的链表中查询是否存在另一个顶点。其实,一切都是以时间来换空间的。

此外,我们还可以对链表进行一些改进升级,比如替换成平衡二叉查找树、跳表、散列表或者是有序动态数组等,来快速查找两个顶点之间是否存在边。

4. 微信微博是怎么存储好友关系的?

微信是无向图,微博是有向图,但二者的解决思路是差不多的,我们拿微博来举例说明。针对微博用户,假设我们需要支持以下这几个操作:

  • 判断用户 A 是否关注了用户 B;
  • 判断用户 A 是否是用户 B 的粉丝;
  • 用户 A 关注用户 B;
  • 用户 A 取消关注用户 B;
  • 根据用户名称的首字母排序,分页获取用户的粉丝列表;
  • 根据用户名称的首字母排序,分页获取用户的关注列表;

因为社交网络是一张稀疏图,因此我们选取邻接表来存储。但是邻接表只能知道某一个用户关注了哪些用户,而不能知道哪些用户关注了他。因此,我们需要一个逆邻接表来表示用户的被关注情况。逆邻接表中,每个顶点的链表存储的是以哪个顶点开始的边指向了这个顶点。

基础的邻接表不适合快速判断两个用户是否是关注与被关注的关系,我们需要对链表进行改进。因为我们需要按照用户名称的首字母排序,分页获取用户的粉丝列表和关注列表,这时候,用跳表这种结构就再合适不过了。跳表插入、删除、查找的时间复杂度都是 $O(logn)$,空间复杂度稍高,为 $O(n)$,但是跳表中的数据本来就是有序的了,分页获取粉丝列表或者关注列表,就非常高效。

针对小规模的数据,我们可以将整个社交图都存储在内存中,但如果数据规模过大,像微博有上亿的用户,这时候就无法全部存储在内存中了。

针对这种情况,我们可以通过哈希算法等数据分片方式,将邻接表存储在不同的机器上。当需要查找顶点之间关系的时候,就利用同样的哈希算法,先定位顶点所在的机器,再去相应的机器上进行查找。

除此之外,我们还可以在外部存储上建立数据库,通过建立索引来高效地支持前面定义的操作。

参考资料-极客时间专栏《数据结构与算法之美》

获取更多精彩,请关注「seniusen」!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值