28| 图的表示:如何存储微信、微博等社交网络中的好友关系?


大家好,我是爱好编程的斌斌。

在互联网如此发达的时代,相信你也一定用过微信或微博。在微信中,可以互相添加好友;在微博中,可以互相关注。那微信和微博是通过什么来存储这种关系的呢?

那就与这篇文章要讲的内容息息相关喽,没错那就是图这种数据结构。说到图,与之相关的算法也有很多,也相对复杂,比如图的搜索、最短路径、最小生成树、二分图等等。

一、如何理解图?

树 V.S. 图

能刷到这篇文章,相信您也和笔者一样,已经对树这种结构有了一定的见解。和树一样,图也是一种非线性表数据结构。在树中,每个元素被称为节点;而在图中,每个节点被称为顶点(vertex)。参考下面这张图可以发现,每个顶点可以与任意其它顶点建立连接关系。这就是图的一个特点,我们把它们建立的这种关系称为边(edge)。

在这里插入图片描述

在生活中有很多关系符合这种逻辑,比如文章开头说的微信和微博。

微信相对比较简单,如果两个用户互为好友关系,就在它们之间建立一条边。这样,微信的好用关系就可以用一张图来表示。每个顶点,也就是微信用户,有多少条边指向,就表示其有多少好友,也叫做顶点的(degree)。

相较于微信来说,微博就比较复杂一点,因为它支持单向关注,也就是我关注了你,而你却没有关注我(hint:制作不易,给个关注吧)。那我们要怎么去表示这种单向关注的关系呢?

这里我们要引入一个新的概念:方向,也就是说在上面的基础上给每条边加上方向。如下图所示,如果用户A关注了用户B,那就从A指出一条带箭头的线段指向B;而如果用户B关注了用户A,那就再引出一条有向边,但这次是从B出发指向A喽.

在这里插入图片描述

总结:图也有分类,把这种使用有向边的图称为“有向图”;相反,把这种没有方向的图称为“无向图”。

在无向图中有这个概念,而在有向图中,它被拆解成了"入度"和"出度"两个概念。见名知意,"入度"就是指向该定点边的条数;"出度"就是该定点指向其他顶点的边的条数。拿微博来说,入度就相当于粉丝列表,出度就相当于关注列表。

与微信和微博相比,还存在一种更加复杂的社交软件–QQ。为什么说它复杂呢?因为它记录了一种关系叫"亲密度"。那啊是如何记录好友之间的亲密度的呢?这里在引入一种图,带权图(weighted graph)。在带权图中,每条边记录的是权重,QQ就是通过这个来表示亲密度的。

在这里插入图片描述

通过三个例子的分析,把图的概念讲完啦,恭喜观看姥爷过关啦!相信你一定好奇图有这么多的顶点和边,是怎么存储下来的呢?会有这么高级的存储方法,反正我是不信,不信就接着看下去吧!你会找到答案.

二、如何存储图这种数据结构?

1.邻接矩阵

1.介绍

邻接矩阵是最直观的存储方式,它的底层基于二维数组。不同的图类型,使用的存储数据的方式是不一样的。对于无向图来说,如果i、j顶点之间有一条边,那就将Aji和Aij同时赋值1;而对于有向图来说,如果i和j顶点之间有一条i指向j的有向边,就将Aij赋值为1,而Aji不变.对与带权图而言,数组存的就是对应的权重,如图所示:

在这里插入图片描述

2.优缺点分析

优点

邻接矩阵的存储方式简单、高效,因为底层基于二维数组,也方便了计算,图可以用邻接矩阵来表示,那么图之间的运算就可以转换成矩阵之间的运算。比如求解邻接矩阵问题会提到一个Floyd-Washall算法,这就是利用矩阵循环相乘若干次得到结果。

缺点

邻接矩阵比较浪费存储空间,拿无向图来说,但顶点i和j之间有一条边时,Aij和Aji都需要存储一样的结构,这就导致了数据重复存储。如果我们按主对角线将矩阵分为上三角和下三角,你会发现我们只需要存储一个三角就行了,也就是有一半的空间被浪费了。又比如我们分析的微信,微信的用户达到了好几亿,而每个人的好友一般只有几百,这就组成了一张稀疏图,也就是顶点很多,但顶点的边却很少。如果我们用邻接矩阵来存储稀疏图,就会有大部分的存储空间被浪费掉。

为了解决邻接矩阵浪费空间这个问题,就引入了一种新的存储方式:邻接表。

2.邻接表

如下图所示,是一张有向图的链接表存储。你有没有发现?这是不是有点像散列表?每个顶点都连着一条链表,链表中存储的是与该顶点相连的其他顶点。对于有向图而言,散列表中,每条链表存储的是指向该顶点的顶点。类似的,对于无向图而言,每个顶点的链表中存储的,是与这个顶点相连的顶点。

在这里插入图片描述

为了更好地理解这两种存储方式,我们将二者进行比较。这里要引入一个思想:用时间换空间以及用空间换时间的思想。相信你也察觉到了,邻接表恰好符合用空间换时间的思想,虽然浪费存储空间,但它查找关系的效率很高;而邻接表恰恰相反,符合的是用时间换空间的思想,不是很耗内存,但链表的查询效率就很低效,对缓存也不太友好。为了这个问题,我们可以在链表比较长的时候,将链表转换成更佳高效的动态数据结构,比如平衡二叉查找树、红黑树、跳表散列表等。也可以将链表换成动态数组,通过使用二分查找来快速定位二哥顶点之间是否存在边。

三、总结:

在这篇文章中,笔者先引入了几个概念:无向图、有向图、带权图、顶点、边、度、入度、出度,在结合日常生活的分析下,相信您已经对它们有了充分的理解。其次,介绍了两种图的存储方式:邻接矩阵和邻接表。

然后,对二者的优缺点进行了分析。邻接矩阵存储方法的缺点是比较浪费空间,但是优点是查询效率高,而且方便矩阵运算。邻接表存储方法中每个顶点都对应一个链表,存储与其相连接的其他顶点。尽管邻接表的存储方式比较节省存储空间,但链表不方便查找,所以查询效率没有邻接矩阵存储方式高。针对这个问题,邻接表还有改进升级版,即将链表换成更加高效的动态数据结构,比如平衡二叉查找树、跳表、散列表等。

四、文末

解答开篇

课后思考

  1. 关于开篇思考题,我们只讲了微博这种有向图的解决思路,那像微信这种无向图,应该怎么存储呢?你可以照着我的思路,自己做一下练习。
  2. 关于图这种数据结构,你还能想到其他生活或者工作中的例子吗?
  • 1:对于微信这种无向图,可以用邻接表来存储每个人对应的好友列表。为了支持快速的查找,好友列表可以使用红黑树存储。
  • 2:生活中图的例子:地图、互联网上页面之间通过超链接连成一张有向图;城市乃至全国交通网络是一种带权图。
  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值