邻接表应优先用 dict(如 defaultdict(list))而非 list 存储节点,因其支持任意可哈希键(字符串、uuid等),查找 o(1),避免强制映射导致的内存翻倍与失效风险。

邻接表该用 dict 还是 list 存储节点?
超大规模图(比如亿级边、千万级节点)下,dict 是更实际的选择。用 list 要求节点 ID 必须是连续非负整数且从 0 开始,现实中几乎不可控——ID 可能是字符串、UUID、稀疏长整型(如 Twitter 用户 ID)。用 dict 支持任意可哈希键,查找节点 O(1),插入也无压力。
常见错误:强行把字符串 ID 映射成连续索引再用 list,结果多一层哈希映射 + 索引转换,内存翻倍、GC 压力大、还容易因 ID 更新失效出错。
- 用
graph = defaultdict(list)或graph = {}+setdefault - 如果需频繁查某条边是否存在,改用
defaultdict(set),但注意set不保序、不支持重复边 - 避免用
list+index()查邻居——那是O(n),邻接表优势全丢
如何高效添加亿级边而不爆内存?
一次性读完所有边再构建图,很容易触发内存峰值——特别是 CSV/JSON 流式数据。正确做法是边读边建,控制中间结构最小化。
典型坑:用 pandas.read_csv 加载整个边表,再 iterrows() 插入,DataFrame 本身就会吃掉几倍于原始文本的内存。
- 用原生
csv.reader或open().readline()流式解析,每行只生成两个节点 ID 和(可选)权重 - 插入时直接调用
graph[u].append(v),别先存到临时列表再 extend - 若边含权重,存为
tuple(如(v, w))或namedtuple,别用dict——每个字典对象有 ~240 字节固定开销 - 考虑用
array.array('I')或numpy.ndarray存纯整数邻居(仅当 ID 确实是小整数且数量稳定)
需要支持反向边或度数查询时怎么设计?
很多算法(如 PageRank、BFS 反向传播、入度统计)要快速获取「谁指向我」或「我的入度/出度」。邻接表天然只存出边,硬查所有节点的邻居列表是 O(|V|+|E|),不可接受。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
不要临时重建反向图——那等于存两份边,内存直接 ×2。应按需叠加轻量结构。
- 出度:直接
len(graph[u]),O(1) - 入度:维护单独的
in_degree = defaultdict(int),加边u → v时同步in_degree[v] += 1 - 反向邻接表:用
rev_graph = defaultdict(list),加边时同时做rev_graph[v].append(u);若只偶尔查,可封装成懒加载属性,避免初始化全量 - 慎用
Counter替代defaultdict(int)——它底层是 dict,但接口更重,且不支持就地更新语法
磁盘溢出或图太大放不下内存怎么办?
当图规模超过物理内存(比如 50GB 边数据配 64GB RAM),Python 的纯内存邻接表必然失败。这时必须放弃「全图驻留」假设,转向外部存储或近似结构。
别指望 shelve 或 sqlite 当邻接表用——单次查邻居会变成磁盘随机 IO,比内存慢 10⁵ 倍,算法直接卡死。
- 优先用内存映射(
mmap)+ 自定义二进制格式:把节点偏移、邻居起止位置预存,用指针跳转,IO 局部性好 - 对静态图,用
networkx.read_edgelist(format='adjlist')配合create_using=nx.DiGraph,它内部做了缓冲,但仍是全内存 - 真正超大规模场景(如工业级图学习),直接切到
PyTorch Geometric的NeighborSampler或DGL的图分区——它们把图切块、异步加载、GPU 友好 - 如果只是需要采样子图,用
random.sample(graph[u], k=100)比遍历全部邻居现实得多
邻接表不是银弹;当节点 ID 不规整、边流式到达、内存见顶时,结构选择本质是做 trade-off:你要快查,还是要省内存,还是得支持动态更新——三者很难同时满足。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










