1. 图论基础:从社交网络到握手定理
第一次接触图论时,我被那些看似复杂的术语吓到了。直到有一天,我把微信好友关系画在纸上——每个人是一个点,每对好友之间画一条线。突然发现,这不就是图论研究的"图"吗?这种直观的理解方式让我瞬间开窍。
在无向图中,度(degree)是最基础也最重要的概念之一。它表示一个顶点连接的边数,就像统计微信好友数量。举个例子,如果你的微信有150个好友,那么在图论中你的"度"就是150。那些没有好友的账号就是孤立点(度为零),而只有一个好友的账号称为悬挂点。
邻接(adjacent)描述的是顶点之间的关系。如果两个人是微信好友,他们的顶点就是邻接的。所有与某个顶点邻接的顶点集合称为它的邻居(neighborhood)。比如你微信中所有好友的集合就是你的邻居。
这里有个有趣的现象:把图中所有顶点的度数加起来,总会得到边数的两倍。这就是著名的握手定理。想象一个聚会,每个人握手次数的总和必然是实际握手次数的两倍(因为每次握手涉及两个人)。这个定理虽然简单,却是图论中许多重要结论的基础。
python复制# 计算图中所有顶点的度数之和
def sum_of_degrees(graph):
return sum(len(adj_list) for adj_list in graph.values())
# 示例:一个简单的社交网络图
social_network = {
'Alice': ['Bob', 'Charlie'],
'Bob': ['Alice', 'Charlie', 'David'],
'Charlie': ['Alice', 'Bob'],
'David': ['Bob']
}
print(sum_of_degrees(social_network)) # 输出:8 (2+3+2+1)
握手定理还有个重要推论:任何图中,度为奇数的顶点一定有偶数个。这就像聚会上,握手次数为奇数的人必定是偶数个。这个性质在网络分析和算法设计中经常用到。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 有向图:微博关注与度数的细分
微博的关注关系就是典型的有向图——我关注你,但你不一定关注我。这种情况下,度的概念需要更精细的划分。入度(in-degree)表示有多少人关注你(粉丝数),出度(out-degree)表示你关注了多少人(关注数)。
有向图中也有类似握手定理的规律:所有顶点的入度之和等于出度之和,且都等于图中的边数。这很好理解,因为每条有向边都会贡献一个出度(对起点)和一个入度(对终点)。
python复制# 计算有向图的入度和出度
def calculate_degrees(digraph):
in_degree = {v: 0 for v in digraph}
out_degree = {v: 0 for v in digraph}
for u in digraph:
out_degree[u] = len(digraph[u])
for v in digraph[u]:
in_degree[v] += 1
return in_degree, out_degree
# 示例:微博关注关系
weibo_follow = {
'大V': ['粉丝A', '粉丝B'],
'粉丝A': ['大V'],
'粉丝B': [],
'路人C': ['大V', '粉丝A']
}
in_d, out_d = calculate_degrees(weibo_follow)
print(in_d) # 输出各用户的粉丝数
print(out_d) # 输出各用户的关注数
实际应用中,我们经常需要将有向图转换为基本无向图——即忽略边的方向。这在社交网络分析中很常见,比如把双向关注的关系视为"好友"关系。但要注意,这种转换会丢失原始有向图中的一些重要信息。
3. 特殊图家族:完全图、圈图与轮图
图论中有几个重要的"家族",它们在算法设计和问题建模中经常出现。第一个是完全图(complete graph)Kₙ,其中每对不同的顶点之间都有一条边相连。就像微信群里每个人都互相加了好友,这样的图边数很多,共有n(n-1)/2条边。
圈图(cycle graph)Cₙ像是围成一个圈的n个人,每人只与左右邻居相连。它的边数正好是n,形成了一个闭合环。在计算机网络中,这种拓扑结构很常见,但有个致命缺点:任何一条边断开都会导致网络分裂。
更有意思的是轮图(wheel graph)Wₙ,它在圈图Cₙ的基础上加了一个"中心"顶点,并与所有外围顶点相连。就像自行车轮子,中心是轮毂,外围是轮圈。Wₙ的边数是2(n-1),这种结构在数据中心网络设计中很有用,因为它比圈图更可靠。
python复制# 生成轮图Wₙ的邻接表表示
def wheel_graph(n):
graph = {i: [] for i in range(n+1)} # 0是中心,1-n是外围
# 连接外围顶点形成圈
for i in range(1, n+1):
next_v = i+1 if i < n else 1
graph[i].append(next_v)
graph[next_v].append(i)
# 连接中心与所有外围顶点
for i in range(1, n+1):
graph[0].append(i)
graph[i].append(0)
return graph
print(wheel_graph(5)) # 输出W₅的邻接表
n立方体图Qₙ则更加抽象,它的顶点表示所有长度为n的二进制串,两个顶点相邻当且仅当它们的二进制串恰好相差一位。这种结构在并行计算和编码理论中非常重要,因为它具有良好的对称性和容错性。
4. 二分图:从相亲关系到任务分配
二分图(bipartite graph)是最具实用价值的特殊图之一。它的顶点可以分成两个不相交的集合,使得每条边都连接两个集合中的顶点。就像相亲活动,一边是男生,一边是女生,所有配对关系都发生在男女之间。
判断一个图是否是二分图有个简单方法:双色法。尝试用两种颜色给顶点着色,要求相邻顶点颜色不同。如果能够做到,就是二分图;否则就不是。这就像给地图上的国家着色,确保相邻国家颜色不同。
python复制# 判断图是否为二分图
def is_bipartite(graph):
color = {}
for node in graph:
if node not in color:
queue = [node]
color[node] = 0
while queue:
v = queue.pop(0)
for neighbor in graph[v]:
if neighbor not in color:
color[neighbor] = color[v] ^ 1
queue.append(neighbor)
elif color[neighbor] == color[v]:
return False
return True
# 示例:相亲关系图
dating_graph = {
'男A': ['女X', '女Y'],
'男B': ['女Y'],
'女X': ['男A'],
'女Y': ['男A', '男B']
}
print(is_bipartite(dating_graph)) # 输出:True
完全二分图Kₘ,ₙ是二分图中边数最多的情况,其中第一个集合的每个顶点都与第二个集合的所有顶点相连。这在任务分配问题中很常见:m个工人和n个任务,每个工人都能胜任所有任务,那么工人和任务之间的关系就构成Kₘ,ₙ。
二分图在网络流、匹配问题等领域有广泛应用。比如在线广告投放,广告主和广告位构成二分图,通过最大匹配算法可以实现收益最大化。又比如课程安排,教师和课程时间也可以建模为二分图。
