元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
算法基础
▶
复杂度分析
时间复杂度
空间复杂度
渐进符号
▶
算法思想
分治法
贪心算法
回溯法
概率算法
枚举算法
递推算法
递归算法
动态规划
分支限界法
模拟算法
▶
算法分析
正确性证明
性能优化
算法比较
▶
搜索算法
▶
基本搜索
顺序搜索
二分搜索
插值搜索
▶
图搜索
深度优先搜索
广度优先搜索
启发式搜索
▶
高级搜索
双向搜索
A*算法
模式匹配
▶
排序算法
▶
比较类排序
冒泡排序
快速排序
归并排序
插入排序
选择排序
▶
非比较类排序
计数排序
基数排序
桶排序
▶
高级排序
堆排序
希尔排序
外部排序
拓扑排序
▶
图论算法
▶
图的表示
邻接矩阵
邻接表
边集数组
▶
最短路径
Dijkstra算法
Floyd算法
Bellman-Ford算法
最短路径概述
▶
生成树
Prim算法
Kruskal算法
并查集
最小生成树
▶
图的连通性
强连通分量
割点与桥
双连通分量
欧拉路径
▶
动态规划
▶
基础概念
最优子结构
重叠子问题
状态转移方程
▶
经典问题
背包问题
最长公共子序列
编辑距离
▶
优化技巧
空间优化
状态压缩
区间动态规划
▶
字符串算法
▶
字符串匹配
KMP算法
Boyer-Moore算法
Sunday算法
▶
字符串处理
字典树
后缀数组
字符串哈希
▶
压缩算法
游程编码
Huffman编码
LZ77算法
发布时间:
2025-03-21 19:09
↑
☰
# 拓扑排序算法 拓扑排序(Topological Sorting)是一种用于有向无环图(DAG)的排序算法,它将图中的顶点按照依赖关系排成一个线性序列。本文将介绍两种经典的拓扑排序算法:Kahn算法和DFS拓扑排序算法。 ## 基本概念 ### 拓扑排序定义 对于有向图G中的任意两个顶点u和v,如果存在一条从u到v的路径,则在拓扑排序中u必须在v之前。拓扑排序的结果不唯一,但都满足上述条件。 ### 应用场景 1. 任务调度 2. 编译依赖分析 3. 课程安排 4. 项目管理 ## Kahn算法 ### 算法原理 Kahn算法是一种基于入度的拓扑排序算法,其基本思想是: 1. 找出所有入度为0的顶点,将它们加入队列 2. 每次从队列中取出一个顶点,将其加入结果序列 3. 删除该顶点的所有出边,更新相邻顶点的入度 4. 如果有新的入度为0的顶点,将其加入队列 5. 重复步骤2-4直到队列为空 ### 代码实现 ```python from collections import defaultdict, deque class Graph: def __init__(self, vertices): self.V = vertices self.graph = defaultdict(list) self.in_degree = [0] * vertices def add_edge(self, u, v): """添加边u->v""" self.graph[u].append(v) self.in_degree[v] += 1 def kahn_topological_sort(self): """使用Kahn算法进行拓扑排序""" # 初始化结果列表和队列 result = [] queue = deque() # 将所有入度为0的顶点加入队列 for i in range(self.V): if self.in_degree[i] == 0: queue.append(i) # 记录访问的顶点数 visited_count = 0 while queue: # 从队列中取出一个顶点 u = queue.popleft() result.append(u) visited_count += 1 # 删除所有从u出发的边 for v in self.graph[u]: self.in_degree[v] -= 1 # 如果顶点的入度变为0,将其加入队列 if self.in_degree[v] == 0: queue.append(v) # 检查是否存在环 if visited_count != self.V: raise ValueError("图中存在环,无法进行拓扑排序") return result # 使用示例 g = Graph(6) # 添加边 edges = [ (5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1) ] for u, v in edges: g.add_edge(u, v) try: result = g.kahn_topological_sort() print("拓扑排序结果:", result) except ValueError as e: print(e) ``` ## DFS拓扑排序 ### 算法原理 DFS拓扑排序是一种基于深度优先搜索的算法,其基本思想是: 1. 对图进行DFS遍历 2. 在顶点的邻接点都访问完后,将该顶点加入结果序列 3. 最终将结果序列反转 ### 代码实现 ```python class Graph: def __init__(self, vertices): self.V = vertices self.graph = defaultdict(list) def add_edge(self, u, v): """添加边u->v""" self.graph[u].append(v) def topological_sort_util(self, v, visited, stack, temp): """DFS拓扑排序的辅助函数""" # 标记当前顶点为临时访问 temp[v] = True visited[v] = True # 递归访问所有邻接顶点 for i in self.graph[v]: # 如果发现环,抛出异常 if temp[i]: raise ValueError("图中存在环,无法进行拓扑排序") if not visited[i]: self.topological_sort_util(i, visited, stack, temp) # 当前顶点处理完成,取消临时标记 temp[v] = False # 将顶点加入栈 stack.append(v) def dfs_topological_sort(self): """使用DFS进行拓扑排序""" visited = [False] * self.V temp = [False] * self.V stack = [] try: # 对每个未访问的顶点进行DFS for i in range(self.V): if not visited[i]: self.topological_sort_util(i, visited, stack, temp) # 返回反转后的结果 return stack[::-1] except ValueError as e: raise e # 使用示例 g = Graph(6) # 添加边 edges = [ (5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1) ] for u, v in edges: g.add_edge(u, v) try: result = g.dfs_topological_sort() print("拓扑排序结果:", result) except ValueError as e: print(e) ``` ## 算法比较 ### 时间复杂度 1. Kahn算法:O(V + E) - 初始化入度数组:O(E) - 处理每个顶点和边:O(V + E) 2. DFS拓扑排序:O(V + E) - DFS遍历:O(V + E) - 栈操作:O(V) ### 空间复杂度 1. Kahn算法:O(V) - 队列:O(V) - 入度数组:O(V) 2. DFS拓扑排序:O(V) - 递归栈:O(V) - 访问数组:O(V) ### 优缺点比较 1. Kahn算法: - 优点: * 实现简单直观 * 可以检测环 * 适合并行处理 - 缺点: * 需要额外空间存储入度 * 需要预处理计算入度 2. DFS拓扑排序: - 优点: * 不需要预处理 * 可以方便地检测环 * 实现优雅 - 缺点: * 递归实现可能导致栈溢出 * 不适合并行处理 ## 应用示例 ### 课程安排 ```python class CourseScheduler: def __init__(self): self.courses = {} self.course_id = 0 self.graph = None def add_course(self, name): """添加课程""" if name not in self.courses: self.courses[name] = self.course_id self.course_id += 1 def add_prerequisite(self, course, prereq): """添加课程依赖关系""" if course in self.courses and prereq in self.courses: if self.graph is None: self.graph = Graph(self.course_id) self.graph.add_edge(self.courses[prereq], self.courses[course]) def get_course_order(self): """获取课程学习顺序""" if self.graph is None: return list(self.courses.keys()) try: # 使用Kahn算法获取拓扑排序 order = self.graph.kahn_topological_sort() # 将课程ID转换回课程名 id_to_course = {v: k for k, v in self.courses.items()} return [id_to_course[i] for i in order] except ValueError as e: raise ValueError("课程之间存在循环依赖,无法安排学习顺序") # 使用示例 scheduler = CourseScheduler() # 添加课程 courses = [ "数据结构", "算法设计", "计算机网络", "操作系统", "数据库系统" ] for course in courses: scheduler.add_course(course) # 添加依赖关系 scheduler.add_prerequisite("算法设计", "数据结构") scheduler.add_prerequisite("操作系统", "数据结构") scheduler.add_prerequisite("数据库系统", "数据结构") scheduler.add_prerequisite("计算机网络", "操作系统") try: order = scheduler.get_course_order() print("推荐的课程学习顺序:") for i, course in enumerate(order, 1): print(f"{i}. {course}") except ValueError as e: print(e) ``` ### 项目构建系统 ```python class BuildSystem: def __init__(self): self.modules = {} self.module_id = 0 self.graph = None def add_module(self, name): """添加模块""" if name not in self.modules: self.modules[name] = self.module_id self.module_id += 1 def add_dependency(self, module, dependency): """添加模块依赖关系""" if module in self.modules and dependency in self.modules: if self.graph is None: self.graph = Graph(self.module_id) self.graph.add_edge(self.modules[dependency], self.modules[module]) def get_build_order(self): """获取构建顺序""" if self.graph is None: return list(self.modules.keys()) try: # 使用DFS拓扑排序获取构建顺序 order = self.graph.dfs_topological_sort() # 将模块ID转换回模块名 id_to_module = {v: k for k, v in self.modules.items()} return [id_to_module[i] for i in order] except ValueError as e: raise ValueError("模块之间存在循环依赖,无法确定构建顺序") # 使用示例 builder = BuildSystem() # 添加模块 modules = [ "core", "ui", "network", "database", "utils" ] for module in modules: builder.add_module(module) # 添加依赖关系 builder.add_dependency("ui", "core") builder.add_dependency("ui", "utils") builder.add_dependency("network", "core") builder.add_dependency("database", "core") builder.add_dependency("core", "utils") try: order = builder.get_build_order() print("项目构建顺序:") for i, module in enumerate(order, 1): print(f"{i}. {module}") except ValueError as e: print(e) ``` ## 总结 1. 算法选择: - 如果需要并行处理或者实时处理,选择Kahn算法 - 如果内存受限或者不需要预处理,选择DFS拓扑排序 2. 实现考虑: - 正确处理环检测 - 选择合适的数据结构 - 考虑并发情况 3. 应用场景: - 依赖分析 - 任务调度 - 工作流管理 - 编译系统