别再死记硬背‘一笔画’了!用Python实现Hierholzer算法,5分钟搞定欧拉回路问题

张开发
2026/4/17 15:49:10 15 分钟阅读

分享文章

别再死记硬背‘一笔画’了!用Python实现Hierholzer算法,5分钟搞定欧拉回路问题
用Python玩转欧拉回路Hierholzer算法实战指南第一次在技术面试中遇到一笔画问题时我盯着那道要求判断图形能否用一笔画完的题目足足发呆了十分钟。直到后来才发现这类看似简单的智力题背后竟藏着图论中经典的欧拉回路问题。更让我惊讶的是用Python实现Hierholzer算法后原本需要死记硬背的定理突然变得直观起来——原来算法可以如此优雅地模拟人类自然的绘图思维。1. 从七桥问题到代码实现18世纪哥尼斯堡的七座桥难题被数学家欧拉抽象为图论中的一笔画问题。想象你是一名邮递员需要找到一条经过每条街道恰好一次的路线或者你正在设计电路板需要寻找不重复焊接的走线方案——这些都是欧拉回路的典型应用场景。欧拉路径与回路的本质区别欧拉路径经过图中每条边恰好一次的路径起点和终点可以不同欧拉回路起点和终点相同的欧拉路径闭合环路判断一个图是否存在欧拉回路只需记住这个简单规则def has_euler_circuit(graph): 检查无向图是否存在欧拉回路 return all(degree % 2 0 for degree in graph.degree_values())2. Hierholzer算法的精妙之处相比需要反复检查桥接性的Fleury算法Hierholzer算法以其O(E)的时间复杂度成为现代应用的首选。它的核心思想就像玩贪吃蛇游戏沿着边前进吃掉经过的边当无路可走时就回溯最终逆序输出路径。算法步骤分解从合适的起点出发全偶数度图中任意点半欧拉图中奇数度点深度优先遍历同时删除已访问的边当顶点无未访问边时将其压入栈最终栈中逆序即为欧拉回路def hierholzer(graph): stack [] path [] current_vertex next(iter(graph)) # 任意起点 stack.append(current_vertex) while stack: current_vertex stack[-1] if graph[current_vertex]: next_vertex graph[current_vertex].pop() stack.append(next_vertex) else: path.append(stack.pop()) return path[::-1]3. 邻接表表示的Python实现让我们用Python的defaultdict来实现一个完整的解决方案from collections import defaultdict class EulerianGraph: def __init__(self): self.adj defaultdict(list) def add_edge(self, u, v): self.adj[u].append(v) self.adj[v].append(u) # 无向图 def find_eulerian_circuit(self): if not self._is_eulerian(): return None # 创建邻接表副本以避免修改原图 temp_adj defaultdict(list) for u in self.adj: temp_adj[u] self.adj[u].copy() stack [] circuit [] current_vertex next(iter(self.adj)) stack.append(current_vertex) while stack: current_vertex stack[-1] if temp_adj[current_vertex]: next_vertex temp_adj[current_vertex].pop() stack.append(next_vertex) else: circuit.append(stack.pop()) return circuit[::-1] def _is_eulerian(self): 检查是否所有顶点度数为偶数 return all(len(edges) % 2 0 for edges in self.adj.values())4. 实际应用中的性能优化在LeetCode等编程挑战中处理大规模图时需要特别注意效率。以下是几个关键优化点优化技巧对比表优化点原始方法优化方法性能提升邻接表存储列表存储边使用集合快速删除O(1)边删除栈实现普通列表预分配固定大小列表减少内存分配度数检查遍历计算维护度数计数器O(1)度数查询# 优化后的边删除操作 graph { A: {B, C}, B: {A, D}, C: {A, D}, D: {B, C} } def remove_edge(graph, u, v): graph[u].remove(v) graph[v].remove(u)5. 常见错误与调试技巧在实现过程中我踩过几个典型的坑忘记处理孤立顶点当图中存在度数为0的顶点时需要特殊处理邻接表修改问题直接修改原始邻接表会导致后续判断出错栈的使用误区错误地将顶点加入结果列表而非栈中调试示例# 错误示范直接修改原始图 def wrong_hierholzer(graph): path [] current next(iter(graph)) while graph[current]: next_node graph[current].pop() # 破坏原始图结构 path.append(next_node) current next_node return path # 正确做法使用临时副本 temp_graph {k: v.copy() for k, v in graph.items()}6. 从算法到现实应用Hierholzer算法不仅适用于理论问题在诸多实际场景中都有出色表现电路板设计确保PCB走线不重复覆盖物流路径规划优化邮递员或垃圾收集车的路线DNA测序解决基因组组装中的片段连接问题网络路由设计不重复使用链路的检测路径# 物流路径规划示例 delivery_graph EulerianGraph() delivery_graph.add_edge(仓库, A区) delivery_graph.add_edge(A区, B区) delivery_graph.add_edge(B区, 仓库) optimal_route delivery_graph.find_eulerian_circuit()7. 算法扩展与变种掌握基础算法后可以进一步探索这些有趣变种有向图的欧拉回路检查入度等于出度加权图的最优欧拉回路结合最小生成树算法混合图的欧拉回路同时包含有向和无向边的情况有向图实现差异def is_directed_eulerian(graph): 检查有向图是否为欧拉图 return all(len(graph[u]) len(in_degree[u]) for u in graph)在最近一次技术面试中面试官给出一个看似复杂的路径规划问题。当我用15行Python代码实现Hierholzer算法并解释其工作原理时明显看到了对方眼中的赞许。那一刻我深刻体会到真正理解一个算法远比死记硬背十种解题模板更有价值。

更多文章