图计算基础:顶点、边、属性、有向图与无向图,邻接矩阵与邻接表

好,咱们今天聊聊图计算的基础。说实话,很多做量化的朋友一听到「图」就头大,觉得这是搞AI的人玩的。但我在交易监控系统里摸爬滚打这么多年,发现图这东西,其实就是把复杂关系画出来,然后让计算机帮我们算。

你想想看,交易监控里最头疼的是什么?是关联账户、资金流转、异常路径。这些天然就是图结构。所以,先把基础打牢,后面才能玩得转。

顶点、边、属性——图的三要素

图由三样东西组成:顶点、边、属性。说白了,顶点就是「谁」,边就是「关系」,属性就是「描述」。

  • 顶点(Vertex):也叫节点。在交易监控里,一个账户、一个交易对手、一个IP地址,都可以是顶点。
  • 边(Edge):连接两个顶点的线。比如A账户向B账户转账,这就是一条边。
  • 属性(Property):附加在顶点或边上的信息。比如账户的余额、转账的金额、时间戳。

我个人习惯把属性想象成「贴标签」。顶点上贴标签,边上也贴标签。这样查起来方便,分析起来也直观。

避坑指南:我曾经在一个项目里,把所有属性都塞到顶点上,边只保留一个ID。结果查转账路径时,每次都要回查顶点属性,性能惨不忍睹。后来我把金额、时间戳这些高频查询的属性直接放在边上,查询速度提升了3倍。

有向图与无向图——方向决定一切

图分两种:有向图和无向图。区别就一个字——方向。

  • 无向图:边没有方向。A和B之间有边,就表示双向关系。比如两个账户属于同一个团伙,这就是无向的。
  • 有向图:边有方向。A→B和B→A是两码事。转账就是典型的有向关系,钱从A流向B,不能反过来。

在交易监控里,我99%的时间都在处理有向图。为什么?因为资金流动是有方向的。你查洗钱路径,必须知道钱从哪来、到哪去。无向图更多用在社交关系分析上,比如查关联账户的共用人脉。

小技巧:如果你不确定用有向还是无向,先问自己一个问题:「这个关系能不能反过来?」能,就用无向;不能,就用有向。简单粗暴。

邻接矩阵与邻接表——存储的艺术

图存到计算机里,主要有两种方式:邻接矩阵和邻接表。这俩各有千秋,选错了,后面性能会很难看。

邻接矩阵

用一个二维数组来存图。行和列都代表顶点,交叉点的值表示是否有边。比如有N个顶点,就开一个N×N的矩阵。

// 邻接矩阵示例(有向图)
// 顶点:A, B, C
// 边:A→B, B→C, C→A

    A  B  C
A   0  1  0
B   0  0  1
C   1  0  0

优点:查询两个顶点之间是否有边,O(1)时间复杂度,快得飞起。
缺点:空间复杂度O(N²),顶点一多,内存直接爆炸。

我记得有一次,我处理一个包含10万个账户的图,用邻接矩阵存,光矩阵就占了40GB内存。服务器直接报警了。嗯,从那以后,我对邻接矩阵就谨慎多了。

邻接表

每个顶点维护一个列表,列表里存它指向的顶点。说白了,就是「谁认识谁」的清单。

// 邻接表示例(有向图)
// 顶点:A, B, C
// 边:A→B, B→C, C→A

A: [B]
B: [C]
C: [A]

优点:空间复杂度O(V+E),V是顶点数,E是边数。稀疏图下非常省内存。
缺点:查询两个顶点之间是否有边,最坏情况O(V),需要遍历列表。

实战建议:交易监控里的图,绝大多数是稀疏图。一个账户通常只跟几十个账户有交易,而不是跟所有账户都有交易。所以,邻接表是更务实的选择。我现在的系统,90%的场景都用邻接表。

知识体系结构图

下面这张图,把本章的核心逻辑串起来了。你可以看到,图的基础要素、分类、存储方式,层层递进。

图计算基础 顶点 (Vertex) 边 (Edge) 属性 (Property) 有向图 (Directed Graph) 无向图 (Undirected Graph) 邻接矩阵 (Adjacency Matrix) 邻接表 (Adjacency List) 查询快 O(1) 空间大 O(N²) 空间省 O(V+E) 三要素 分类 存储方式 特点

选型对比表

最后,我整理了一个对比表,方便你快速决策。

维度 邻接矩阵 邻接表
空间复杂度 O(N²) O(V+E)
查询边是否存在 O(1) O(V) 最坏
遍历所有边 O(N²) O(V+E)
适合场景 稠密图、顶点少 稀疏图、顶点多
交易监控适用性 低(顶点通常很多) 高(稀疏图为主)

注意:别被「邻接矩阵查询快」这个优点迷惑了。在交易监控里,顶点数量动辄百万级,邻接矩阵的内存开销根本扛不住。我见过有人硬上邻接矩阵,结果服务器内存被打满,系统直接挂了。选型时,先算算顶点数,再决定。

好了,图计算的基础就这些。顶点、边、属性是骨架,有向无向是方向,邻接矩阵和邻接表是存储。把这些搞明白,后面讲图遍历、路径分析、社区发现,你就能跟上节奏了。


专注资料整理