分子图表示方法:三种基础数据结构

聊到分子图结构,我第一个想跟你分享的就是“表示方法”。说白了,就是怎么把一个分子存进计算机。我在早期做化学数据库时,吃过不少格式不统一的亏,后来才明白——选对表示方法,后面的分析工作能省一半力气。

分子图本质上是个无向图(有时带键级属性),原子是节点,化学键是边。那么问题来了:存成哪种数据结构最合适?我个人习惯根据场景选,主要有三种——邻接矩阵、边列表、化学文件格式。

邻接矩阵

邻接矩阵是图论里最直接的表示法。对于一个有 V 个原子的分子,开一个 V×V 的方阵。如果原子 i 和原子 j 之间有键,矩阵元素 A[i][j] 设为键级,否则为 0。

举个例子,乙醇 (C₂H₅OH) 的邻接矩阵(仅重原子部分):

     C1  C2  O
C1   0   1   0
C2   1   0   1
O    0   1   0

你想想看,这结构多清晰。查找两个原子是否相连,O(1) 时间就能搞定。但代价也很明显——空间复杂度 O(V²)。一个含 100 个原子的分子就要 10000 个格子,大部分还是空的。

我的经验:邻接矩阵适合稠密图或小分子(少于 50 个原子)。如果你在做环检测或路径搜索,邻接矩阵配合 Floyd-Warshall 算法非常顺手。我之前在药物筛选项目中就用它做子结构匹配,速度能接受。
避坑指南:我曾经在分析一个含金属配合物的分子时,忽略了配位键的键级不是整数。结果邻接矩阵里存了 1.5,后续算法直接崩了。要是遇到这种情况,可以分开存“键类型”和“键级”两个矩阵。

边列表

边列表就简单了,它就是所有化学键的集合。每一条记录包含两个原子索引和键的类型。

还是乙醇的例子:

0 1 1.0   # C1 - C2 单键
1 2 1.0   # C2 - O  单键

这里我把 C1 编为 0 号,C2 编为 1 号,O 编为 2 号。每条边三个数字:起点、终点、键级。

边列表的优势是节省空间。一个稀疏分子(比如蛋白质)可能有几千个原子,但每个原子平均只连 2-3 个键。用边列表存储,空间复杂度是 O(E),比邻接矩阵省得多。

表示方法空间复杂度查询两原子是否相连适用场景
邻接矩阵O(V²)O(1)稠密图、小分子
边列表O(E)O(E)(最坏)稀疏图、大分子
化学文件格式O(V+E+属性)需解析存储、交换

边列表的查询性能是短板。你想知道两个原子有没有键,得遍历整个列表。所以在我做的图算法里,边列表通常只作为原始输入,后续会转成其他结构再计算。

实用技巧:把边列表排序后存成二分查找的结构,查询能降到 O(log E)。或者再建一个邻接列表(HashMap 或 list of lists),取邻居时快很多。我自己在库里就默认提供这两种转换。

化学文件格式:SDF 与 MOL

前面两种是纯数据结构,但工业界和学术界更常用的是标准文件格式。最经典的就是 MDL Molfile 和它的大哥 SDF(Structure-Data File)。

Molfile 的原子块(Atom Block)格式固定:

x y z 元素符号 同位素 电荷 等等

键块(Bond Block)更简单:

起点 终点 键类型 键立体化学

一个完整的乙醇 MOL 文件大概长这样:

ethanol.mol
  ChemDraw01011123393D

  3  2  0  0  0  0  0  0  0  0  1 V2000
    0.8660    0.5000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    0.0000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    1.7320    0.0000    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
  2  3  1  0  0  0  0
M  END
$$$$

SDF 就是一个 MOL 文件后面挂着一些键值对属性(分子名称、LogP、SMILES 等),再以 $$$$ 结束。多个分子就拼接在一起。所以 SDF 其实是一个“多分子容器”。

避坑指南:我曾经解析过一批从供应商拿到的 SDF 文件,发现有的文件中原子坐标有 6 位小数,有的是 4 位;有的空行有 tab,有的没有。解析时一定要做好容错。我建议用 RDKit 或 Open Babel 已经写好的解析器,别自己手写正则——血泪教训。

这三种表示方法各有各的舞台。在我日常工作中,边列表常用于算法原型验证,邻接矩阵用于图卷积网络(GCN)的输入,而 SDF 是数据交换的“硬通货”。掌握它们,你就拥有了“看分子”的三种不同视角。

核心认知:选择哪种表示方法,本质上是在“存储体积”和“计算速度”之间做权衡。没有银弹,只有最合适的场景。我通常在项目前期把三种都实现,然后压测,选最优的。
分子图表示方法知识体系 分子结构 邻接矩阵 边列表 化学文件格式 稠密图适用 O(1) 查询 稀疏图适用 存储高效 MOL/SDF 数据交换 三种表示方法的关系 邻接矩阵适合计算,边列表适合遍历,MOL/SDF适合存储交换 实际项目中常需要相互转换

理解这三种表示方法,是构建分子分析工具的必修课。我在搭建第一个分子数据库时,就从边列表起步,后面为了让子结构搜索更快才加了邻接矩阵。一步步来,你也能找到自己项目的“最优解”。