算法: 图论 (1)

1114 字
6 分钟
算法: 图论 (1)
2026-03-20

引入#

图论 (Graph Theory) 是数学和计算机科学中的一个重要分支, 主要研究 图(Graph) 这种数学结构。

图的存储方式#

在计算机中存储图(Graph)主要有三种常见的方式。选择哪种方式取决于你的图是稀疏的还是稠密的,以及你主要需要执行哪些操作(比如是频繁查询两点是否有边,还是频繁遍历某个点的所有邻居)。

邻接矩阵 (Adjacency Matrix)#

这是最直观的方式, 使用一个二维数组来表示。假设图有NN个顶点, 我们创建一个N×NN \times N的矩阵, 如果顶点ii和顶点jj之间有边, 则matrix[i][j] = 1(或者存储权重); 如果没有边, 则matrix[i][j] = 0(或无穷大)。

优点缺点
查询快: 判断两个点之间是否有边,时间复杂度是O(1)O(1)空间浪费: 即使图里只有几条边,也要开辟N×NN \times N的空间。
对于稀疏图(点很多,边很少),浪费极其严重。空间复杂度为O(N2)O(N^2)
实现简单: 代码非常容易写。遍历慢: 想要找出一个点的所有邻居,必须遍历整行,时间复杂度O(N)O(N)
适合稠密图: 当边非常多(接近N2N^2)时,空间利用率较高。

代码示例#

class Graph:
def __init__(self, num_vertices):
self.V = num_vertices
self.matrix = [[0] * self.V for _ in range(self.V)]
def add_edge(self, u, v, weight=1):
self.matrix[u][v] = weight
self.matrix[v][u] = weight # 如果是无向图
def has_edge(self, u, v):
return self.matrix[u][v] != 0

邻接表 (Adjacency List)#

这是最常用的存储方式, 特别是对于稀疏图。创建一个大小为NN的数组,数组的每个元素对应一个顶点。每个顶点后面挂着一个链表 (或动态数组),链表中存储了与该顶点直接相连的所有邻居。

优点缺点
节省空间: 只存储存在的边。空间复杂度为O(V+E)O(V+E)
(VV是点数,EE是边数)。非常适合稀疏图
查询慢: 判断两个点之间是否有边,需要遍历链表,
时间复杂度 O(degree)O(degree),最坏情况 O(V)O(V)
遍历快: 找出一个点的所有邻居非常高效,
时间复杂度与邻居数量成正比 O(degree)O(degree)
实现稍复杂: 比矩阵稍微麻烦一点。

代码示例#

class Graph:
def __init__(self, num_vertices):
self.V = num_vertices
# 初始化列表的列表
self.adj = [[] for _ in range(self.V)]
def add_edge(self, u, v, weight=1):
# 存储邻居和权重 (v, weight)
self.adj[u].append((v, weight))
self.adj[v].append((u, weight)) # 如果是无向图
def get_neighbors(self, u):
return self.adj[u]

边集数组 (Edge List)#

将所有的边 (Edge) 以数组的形式存起来, 每条边是一个对象或元组(起点, 终点, 权重)

优点缺点
极其节省空间: 只需要 O(E)O(E)查询极慢: 想要知道点A的邻居是谁,或者A和B是否相连,
必须遍历整个边列表,效率很低。
便于排序: 如果算法需要对边按权重排序
(如 Kruskal 最小生成树算法),这种方式最方便。
适用场景窄: 通常只用于特定的算法,不作为通用的图存储结构。

深度优先搜索 (DFS)#

深度优先搜索 dfs 一般会递归调用自身, 同时会对其访问过的点打上访问标记, 在遍历图时跳过已打过标记的点, 以确保每个点仅访问一次。

dfs 的大致实现如下:

def dfs(v):
visited[v] = True
for u in adj[v]:
if not visited[u]:
dfs(u)
Warning

无论是 DFS 还是 BFS,都必须维护一个visited集合,防止在有环的图中无限循环。

广度优先搜索 (BFS)#

广度优先, 就是从起点开始,先访问所有直接相邻的节点 (第一层),然后再访问这些相邻节点的邻居 (第二层),像水波扩散一样一层层向外推进。 算法过程就像在人群中传播消息。你先告诉你的所有朋友(第一层),然后让你的朋友告诉他们的所有朋友(第二层),以此类推。 它的特点是离起点近的先被访问

bfs 的大致实现如下:

from collections import deque
def bfs(s):
q = deque([s])
visited[s] = True
while q: # q 非空
u = q.popleft()
for v in adj[u]:
if not visited[v]:
visited[v] = True
q.append(v)

练习题#

参考#

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
算法: 图论 (1)
https://llm-tech.com.cn/posts/graph-1/
作者
Ming
发布于
2026-03-20
许可协议
CC BY-NC-SA 4.0
随机文章 随机推荐
Profile Image of the Author
Ming
你是来找 Ming 学习的吗
🎉 欢迎来到 Ming 的博客
这里是我的个人博客,分享 AI Infra、LLM 等技术内容。欢迎关注交流!
分类
标签
站点统计
文章
2
分类
2
标签
2
总字数
2,370
运行时长
0
最后活动
0 天前

目录