图计算基础:顶点、边、属性、有向图与无向图,邻接矩阵与邻接表
好,咱们今天聊聊图计算的基础。说实话,很多做量化的朋友一听到「图」就头大,觉得这是搞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%的场景都用邻接表。
知识体系结构图
下面这张图,把本章的核心逻辑串起来了。你可以看到,图的基础要素、分类、存储方式,层层递进。
选型对比表
最后,我整理了一个对比表,方便你快速决策。
| 维度 | 邻接矩阵 | 邻接表 |
|---|---|---|
| 空间复杂度 | O(N²) | O(V+E) |
| 查询边是否存在 | O(1) | O(V) 最坏 |
| 遍历所有边 | O(N²) | O(V+E) |
| 适合场景 | 稠密图、顶点少 | 稀疏图、顶点多 |
| 交易监控适用性 | 低(顶点通常很多) | 高(稀疏图为主) |
注意:别被「邻接矩阵查询快」这个优点迷惑了。在交易监控里,顶点数量动辄百万级,邻接矩阵的内存开销根本扛不住。我见过有人硬上邻接矩阵,结果服务器内存被打满,系统直接挂了。选型时,先算算顶点数,再决定。
好了,图计算的基础就这些。顶点、边、属性是骨架,有向无向是方向,邻接矩阵和邻接表是存储。把这些搞明白,后面讲图遍历、路径分析、社区发现,你就能跟上节奏了。