扫码关注公众号,获取更多内容

目录

一、如何理解图?

二、如何存储图

1、邻接矩阵存储法

2、邻接表存储法

三、如何存储诸如微博、微信等社交网络中的好友关系


一、如何理解图?

树中的元素我们成为节点,图中的元素我们叫做顶点。图中的一个顶点可以与任意其他顶点建立连接关系。我们把这种连接关系叫做

我来举一个生活中的例子 — 微信。我们可以把每个用户看作一个顶点。如果两个用户是好友,就在两者直接建立一条边。整个微信的好友关系就可以用一张图来表示。其中,每个用户有多少好友,对应到图中,就叫做顶点的(跟顶点相连接的边的条数)。

微博的社交关系跟微信有点不一样,微博是允许单向关注(用户A关注用户B,但是用户B可以不关注用户A),这种关系用图怎么表示呢?

我们可以在图中引入边的“方向”的概念。

用户A关注了B,就在图中画一条A到B的带箭头的边。如果A和B是互相关注的,就画一条A指向B的边,再画一条B指向A的边。这种有方向的图叫做“有向图”。以此类推,边没有方向的图,叫做“无向图”。

无向图中有“度”这个概念,表示一个顶点有多少条边。在有向图中,我们把度分为入度出度

顶点的入度,表示有多少条边指向这个顶点;顶点的出度,表示有多少条边是以这个顶点为起点指向其他顶点。放到微博的例子中,入度就是代表有多少粉丝,出度就是关注了多少人。

我们身边还有一个例子:QQ。

QQ的社交关系更复杂一些,QQ有亲密度这样一个功能。如果两个用户经常往来,那亲密度就比较高;如果不经常往来,亲密度就比较低。

我们可以用带权图来记录这种关系。在带权图中,每条边都有一个权重。可以通过这个权重来表示QQ好友之间的亲密度。

 

二、如何存储图

1、邻接矩阵存储法

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

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

对于有向图来说,如果顶点i到顶点j之间,有一条箭头是从顶点 i 指向 j 的边,那我们就将 A[i][j] 标记为 1。同样的,如果有一条箭头从顶点j指向顶点i的边,我们就将 A[j][i] 标记为 1。对于带权图,数组中就存储相应的权重。

用邻接矩阵来表示一个图,比较简单、直观,但是也比较浪费存储空间。这是为什么呢?

对于无向图来说,如果 A[i][j] 等于 1,那 A[j][i] 也肯定等于 1,本质上,我们只需要存储一个就可以了。无向图的二维数组中,我们将其用对角线划分为上下两部分,只需要利用上面或者下面这样一半的空间就足够了,另一半浪费掉了。

再者,如果我们存储的是稀疏图(顶点很多,但是每个顶点的边并不多),那邻接矩阵的存储方法就更加浪费空间了。

2、邻接表存储法

邻接矩阵比较浪费内存空间,我们来看另外一种图的存储方法:邻接表

我们看下图,乍一看,邻接表是不是有点像散列表?每个顶点对应一条链表,链表中存储的是与这个顶点相连接的其他顶点。

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

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

三、如何存储诸如微博、微信等社交网络中的好友关系

数据结构是为算法服务的,具体存储方法的选择与期望支持的操作有关系。针对微博用户关系,假设需要支持下面几个操作:

1、判断用户 A 是否关注了用户 B。

2、判断用户 A 是否是用户 B 的粉丝。

3、用户 A 关注用户 B;用户 A 取消关注用户 B。

4、根据用户名称的首字母排序,分页获取用户的粉丝列表。

5、根据用户名称的首字母排序,分页获取用户的关注列表。

关于如何存储一个图,前面讲到两种主要存储方法,邻接矩阵和邻接表。社交网络是一张稀疏图,使用邻接矩阵存储是比较浪费存储空间的。所以,这里我们采用邻接表来存储。

单单用一个邻接表来存储这种有向图是不够的。这是因为我们去查找某个用户关注了哪些用户非常容易,但是如果想要知道某个用户被哪些用户关注了(粉丝列表),是非常困难的。

为了解决这个问题,我们需要一个逆邻接表。邻接表中存储了用户的关注关系,逆邻接表中存储的是用户的被关注关系。对应到图中,邻接表中,每个顶点的链表中,存储的就是这个顶点指向的顶点。

逆邻接表中,每个顶点的链表中,存储的是指向这个顶点的顶点。如果要查找某个用户关注了哪些用户,我们可以在邻接表中查找。如果要查找某个用户被哪些用户关注了,我们从逆邻接表中查找。

基础的邻接表不适合快速判断两个用户直接是否是关注与被关注的关系,所以我们选择改进,将邻接表中的链表改为支持快速查找的动态数据结构。

因为我们要按照用户名称首字母排序,分页来获取用户的粉丝列表或者关注列表,用跳表这种结构比较合适。因为跳表的插入、删除、查找都非常高效。最重要的一点是跳表中存储的数据本来就是有序的,分页获取粉丝列表或者关注列表就非常高效。

对于小规模数据,比如社交网络中只有几万、几十万个用户,我们可以将整个社交关系存储在内存中,上面的思路是没有问题的。但是用户数量级比较高,有上亿,数据规模太大,我们就无法全部存储在内存中了。这时候应该怎么办呢?

我们可以通过哈希算法等数据分片方式,将邻接表存储在不同的机器上,如下图所示:我们在机器1上存储顶点1,2,3是我邻接表,在机器2上存储顶点4,5的邻接表。逆邻接表的处理方式也一样。当要查询顶点与顶点关系的时候,我们就利用同样的哈希算法,先定位顶点所在的机器,然后再在相应的机器上查找。

除了这种方法,我还也可以利用外部存储(比如硬盘),因为外部存储的存储空间会比内存宽裕很多。我们可以采用数据库存储的方式,利用下面这张表来存储遮掩搞一个图,为了高效地支持前面定义的操作,我们可以在表上建立多个索引,比如第一列,第二列,给他们两个都建立索引。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值