本文目录导读:

我来详细讲解如何使用NetworkX进行拓扑排序,包含多个实际案例。
基础拓扑排序
import networkx as nx
import matplotlib.pyplot as plt
# 创建一个有向无环图(DAG)
G = nx.DiGraph()
# 添加边(任务依赖关系)
edges = [
('A', 'B'), # A → B:A完成后才能开始B
('A', 'C'), # A → C:A完成后才能开始C
('B', 'D'), # B → D:B完成后才能开始D
('C', 'D'), # C → D:C完成后才能开始D
('D', 'E') # D → E:D完成后才能开始E
]
G.add_edges_from(edges)
# 执行拓扑排序
try:
topological_order = list(nx.topological_sort(G))
print("拓扑排序结果:", topological_order)
# 输出: ['A', 'B', 'C', 'D', 'E'] 或 ['A', 'C', 'B', 'D', 'E']
except nx.NetworkXUnfeasible:
print("图中存在环,无法进行拓扑排序")
课程安排系统案例
import networkx as nx
class CourseScheduler:
def __init__(self):
self.graph = nx.DiGraph()
def add_course_prerequisites(self, course, prerequisites):
"""添加课程及其先修课程"""
for prereq in prerequisites:
self.graph.add_edge(prereq, course) # 先修课程 → 当前课程
def get_study_order(self):
"""获取学习顺序"""
try:
schedule = list(nx.topological_sort(self.graph))
return schedule
except nx.NetworkXUnfeasible:
print("课程依赖关系存在循环,无法安排学习顺序")
return []
def check_cycle(self):
"""检查是否有循环依赖"""
try:
nx.find_cycle(self.graph)
return True
except nx.NetworkXNoCycle:
return False
def visualize_schedule(self):
"""可视化课程依赖关系"""
pos = nx.spring_layout(self.graph)
nx.draw(self.graph, pos, with_labels=True,
node_color='lightblue',
node_size=2000,
arrows=True,
arrowstyle='->')
plt.title("课程依赖关系图")
plt.show()
# 使用案例
scheduler = CourseScheduler()
# 添加课程依赖
scheduler.add_course_prerequisites("Algorithms", ["Data Structures", "Mathematics"])
scheduler.add_course_prerequisites("Data Structures", ["Programming Basics"])
scheduler.add_course_prerequisites("Machine Learning", ["Algorithms", "Statistics"])
scheduler.add_course_prerequisites("Statistics", ["Mathematics"])
scheduler.add_course_prerequisites("Mathematics", ["Programming Basics"])
# 获取学习顺序
study_order = scheduler.get_study_order()
print("推荐学习顺序:", study_order)
# 输出类似: ['Programming Basics', 'Mathematics', 'Data Structures', 'Statistics', 'Algorithms', 'Machine Learning']
项目管理依赖排序
import networkx as nx
from datetime import datetime, timedelta
class ProjectTaskScheduler:
def __init__(self):
self.graph = nx.DiGraph()
self.task_durations = {}
def add_task(self, task_name, duration_days, dependencies=None):
"""添加任务及其依赖"""
self.task_durations[task_name] = duration_days
if dependencies:
for dep in dependencies:
self.graph.add_edge(dep, task_name)
else:
self.graph.add_node(task_name)
def get_task_order(self):
"""获取任务执行顺序"""
try:
return list(nx.topological_sort(self.graph))
except nx.NetworkXUnfeasible:
print("任务依赖存在循环")
return []
def get_critical_path(self):
"""获取关键路径(最长路径)"""
try:
# 找到所有拓扑排序
task_order = list(nx.topological_sort(self.graph))
# 计算每个任务的最早开始时间
earliest_start = {task: 0 for task in task_order}
for task in task_order:
for pred in self.graph.predecessors(task):
earliest_start[task] = max(
earliest_start[task],
earliest_start[pred] + self.task_durations[pred]
)
# 计算每个任务的最晚开始时间
latest_start = {task: float('inf') for task in task_order}
for task in reversed(task_order):
if self.graph.out_degree(task) == 0:
latest_start[task] = earliest_start[task]
else:
for succ in self.graph.successors(task):
latest_start[task] = min(
latest_start[task],
latest_start[succ] - self.task_durations[task]
)
# 找出关键路径上的任务(最早开始时间等于最晚开始时间)
critical_tasks = [task for task in task_order
if earliest_start[task] == latest_start[task]]
return critical_tasks, earliest_start, latest_start
except Exception as e:
print(f"计算关键路径时出错: {e}")
return [], {}, {}
def get_project_duration(self):
"""获取项目总持续时间"""
critical_tasks, _, _ = self.get_critical_path()
return sum(self.task_durations[task] for task in critical_tasks)
# 使用案例
project = ProjectTaskScheduler()
# 添加项目任务
project.add_task("需求分析", 5)
project.add_task("系统设计", 3, ["需求分析"])
project.add_task("数据库设计", 2, ["系统设计"])
project.add_task("前端开发", 8, ["系统设计"])
project.add_task("后端开发", 10, ["系统设计", "数据库设计"])
project.add_task("测试", 4, ["前端开发", "后端开发"])
project.add_task("部署", 1, ["测试"])
project.add_task("用户培训", 2, ["部署"])
# 获取任务顺序
task_order = project.get_task_order()
print("任务执行顺序:", task_order)
# 获取关键路径
critical_tasks, earliest, latest = project.get_critical_path()
print("\n关键路径上的任务:", critical_tasks)
print("项目总工期:", project.get_project_duration(), "天")
# 显示任务时间安排
print("\n任务时间安排:")
for task in task_order:
print(f"{task}: 最早开始={earliest[task]}天, 最晚开始={latest[task]}天, "
f"工期={project.task_durations[task]}天")
并行任务拓扑排序
import networkx as nx
from collections import defaultdict
class ParallelTaskScheduler:
def __init__(self):
self.graph = nx.DiGraph()
def add_dependency(self, from_task, to_task):
"""添加任务依赖"""
self.graph.add_edge(from_task, to_task)
def get_parallel_levels(self):
"""获取可以并行执行的任务层级"""
try:
topological_order = list(nx.topological_sort(self.graph))
# 计算每个任务的层级
levels = {}
for task in topological_order:
predecessors = list(self.graph.predecessors(task))
if not predecessors:
levels[task] = 0
else:
levels[task] = max(levels[pred] for pred in predecessors) + 1
# 按层级分组
level_groups = defaultdict(list)
for task, level in levels.items():
level_groups[level].append(task)
return dict(level_groups)
except nx.NetworkXUnfeasible:
print("存在环,无法并行调度")
return {}
def visualize_parallel_tasks(self):
"""可视化并行任务"""
levels = self.get_parallel_levels()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
# 子图1:显示DAG
pos = nx.spring_layout(self.graph)
nx.draw(self.graph, pos, with_labels=True,
node_color='lightgreen',
node_size=2000,
arrows=True,
ax=ax1)
ax1.set_title("任务依赖图")
# 子图2:显示层级
for level, tasks in levels.items():
y_pos = [level] * len(tasks)
x_pos = range(len(tasks))
ax2.scatter(x_pos, y_pos, s=2000, alpha=0.6)
for i, task in enumerate(tasks):
ax2.annotate(task, (i, level), ha='center', va='center')
ax2.set_xlabel("并行任务")
ax2.set_ylabel("执行层级")
ax2.set_title("并行任务层级")
ax2.grid(True)
plt.tight_layout()
plt.show()
# 使用案例
parallel_scheduler = ParallelTaskScheduler()
# 添加依赖关系
parallel_scheduler.add_dependency("Task1", "Task4")
parallel_scheduler.add_dependency("Task1", "Task5")
parallel_scheduler.add_dependency("Task2", "Task5")
parallel_scheduler.add_dependency("Task3", "Task6")
parallel_scheduler.add_dependency("Task4", "Task7")
parallel_scheduler.add_dependency("Task5", "Task7")
parallel_scheduler.add_dependency("Task6", "Task8")
# 获取并行层级
parallel_levels = parallel_scheduler.get_parallel_levels()
print("并行执行计划:")
for level, tasks in sorted(parallel_levels.items()):
print(f"层级 {level}: {', '.join(tasks)}")
高级功能:多条件拓扑排序
import networkx as nx
class AdvancedTopologicalScheduler:
def __init__(self):
self.graph = nx.DiGraph()
self.priorities = {} # 任务优先级
def add_task(self, task, priority=0, dependencies=None):
"""添加带优先级的任务"""
self.priorities[task] = priority
if dependencies:
for dep in dependencies:
self.graph.add_edge(dep, task)
else:
self.graph.add_node(task)
def topological_sort_with_priority(self):
"""按优先级排序的拓扑排序"""
try:
# 获取所有拓扑排序(生成器)
all_topsorts = nx.all_topological_sorts(self.graph)
# 根据优先级评分选择最优排序
best_score = -1
best_order = None
for topsort in all_topsorts:
# 计算当前排序的评分
score = self._calculate_order_score(topsort)
if score > best_score:
best_score = score
best_order = topsort
return best_order, best_score
except nx.NetworkXUnfeasible:
print("存在环")
return [], 0
def _calculate_order_score(self, order):
"""计算排序评分"""
score = 0
for i, task in enumerate(order):
# 优先级越高的任务越早完成得分越高
priority = self.priorities.get(task, 0)
score += priority * (len(order) - i)
return score
def get_possible_orders(self, max_results=5):
"""获取多种可能的排序"""
try:
all_topsorts = list(nx.all_topological_sorts(self.graph))
return all_topsorts[:max_results]
except nx.NetworkXUnfeasible:
return []
# 使用案例
advanced_scheduler = AdvancedTopologicalScheduler()
# 添加带优先级的任务
advanced_scheduler.add_task("紧急Bug修复", priority=10)
advanced_scheduler.add_task("功能开发A", priority=5, dependencies=["数据库优化"])
advanced_scheduler.add_task("功能开发B", priority=3, dependencies=["功能开发A"])
advanced_scheduler.add_task("数据库优化", priority=8)
advanced_scheduler.add_task("代码审查", priority=6, dependencies=["功能开发A", "功能开发B"])
advanced_scheduler.add_task("测试", priority=4, dependencies=["代码审查"])
advanced_scheduler.add_task("文档更新", priority=1, dependencies=["功能开发A"])
# 获取最优排序
best_order, score = advanced_scheduler.topological_sort_with_priority()
print(f"最优排序 (评分: {score}): {best_order}")
# 获取所有可能的排序
print("\n所有可能的排序:")
for i, order in enumerate(advanced_scheduler.get_possible_orders(3), 1):
print(f"方案{i}: {order}")
实用工具函数
def check_dag_health(graph):
"""检查DAG图健康状况"""
results = {
'is_dag': nx.is_directed_acyclic_graph(graph),
'node_count': graph.number_of_nodes(),
'edge_count': graph.number_of_edges(),
'has_cycle': False,
'roots': [],
'leaves': []
}
if not results['is_dag']:
results['has_cycle'] = True
try:
cycle = nx.find_cycle(graph)
results['cycle'] = cycle
except nx.NetworkXNoCycle:
pass
else:
# 找出根节点(没有入边的节点)
results['roots'] = [n for n in graph.nodes() if graph.in_degree(n) == 0]
# 找出叶子节点(没有出边的节点)
results['leaves'] = [n for n in graph.nodes() if graph.out_degree(n) == 0]
return results
def layered_topological_sort(graph):
"""分层拓扑排序(用于并行执行)"""
if not nx.is_directed_acyclic_graph(graph):
return None
# 复制图
G = graph.copy()
layers = []
while G.nodes():
# 找出当前层(入度为0的节点)
current_layer = [n for n in G.nodes() if G.in_degree(n) == 0]
if not current_layer:
break
layers.append(current_layer)
# 移除当前层的节点
G.remove_nodes_from(current_layer)
return layers
# 完整的综合示例
def main():
print("=== 拓扑排序综合示例 ===\n")
# 1. 创建任务依赖图
G = nx.DiGraph()
tasks = [
('A', 'B'),
('A', 'C'),
('B', 'D'),
('C', 'D'),
('C', 'E'),
('D', 'F'),
('E', 'F'),
]
G.add_edges_from(tasks)
# 2. 检查DAG健康
health = check_dag_health(G)
print(f"图状态: {'正常' if health['is_dag'] else '有环'}")
print(f"节点数: {health['node_count']}")
print(f"边数: {health['edge_count']}")
print(f"根节点: {health['roots']}")
print(f"叶子节点: {health['leaves']}")
# 3. 标准拓扑排序
if health['is_dag']:
standard_order = list(nx.topological_sort(G))
print(f"\n标准拓扑排序: {standard_order}")
# 4. 分层排序
layers = layered_topological_sort(G)
print("\n分层排序 (并行层级):")
for i, layer in enumerate(layers):
print(f" 层级 {i+1}: {layer}")
# 5. 可视化
pos = nx.spring_layout(G)
plt.figure(figsize=(10, 6))
# 为不同层级着色
colors = ['red', 'blue', 'green', 'orange', 'purple']
node_colors = []
node_to_layer = {}
for i, layer in enumerate(layers):
for node in layer:
node_to_layer[node] = i
for node in G.nodes():
if node in node_to_layer:
node_colors.append(colors[node_to_layer[node] % len(colors)])
else:
node_colors.append('gray')
nx.draw(G, pos, with_labels=True,
node_color=node_colors,
node_size=2000,
arrows=True,
font_size=12,
font_weight='bold')
plt.title("任务依赖图 (颜色表示执行层级)")
plt.show()
if __name__ == "__main__":
main()
关键要点
- 核心函数:
nx.topological_sort()是主要函数 - 错误处理: 始终使用 try-except 处理环检测
- 并行执行: 可以使用分层排序识别可并行任务
- 优先级: 可以通过自定义评分函数实现优先级排序
- 可视化: NetworkX 提供了丰富的可视化选项
这些案例覆盖了拓扑排序的常见应用场景,你可以根据具体需求选择合适的实现方式。