拓扑排序
一、基本概念
1.定义
拓扑排序是对一个有向无环图(DAG)中所有顶点的一种线性排序,使得对于图中的每一条有向边 (u, v),顶点 u 在顶点 v 之前。这种排序确保了在执行任务时,所有依赖于某个任务的任务都在该任务完成之后开始。
2.特点
- 有向无环图:拓扑排序只适用于有向无环图(DAG),因为环会导致依赖关系的循环,使得无法确定顺序。
- 不唯一性:拓扑排序可能不唯一,同一个图可以有多个有效的拓扑排序。
3.例子
考虑一个项目任务的依赖关系:
- 任务A必须在任务B之前完成。
- 任务C必须在任务B之前完成。
- 任务D必须在任务B之前完成。
这种关系可以表示为图:
可能的拓扑排序有:
ACDB
ADCB
注意:每个DAG都有至少一个拓扑排序,但并非所有的有向图都有拓扑排序。
二、Kahn算法求解
1. 算法概述
Kahn算法是一种用于拓扑排序的算法,利用图的入度(指向某个顶点的边的数量)来找到可以先执行的节点。它逐步构建拓扑排序,确保所有依赖关系得到满足。
2. 算法步骤
- 计算入度:
- 初始化一个入度数组,统计每个顶点的入度。
- 对于每条有向边 (u, v),增加顶点 v 的入度。
- 初始化队列:
- 创建一个队列,将所有入度为0的顶点入队。这些顶点没有依赖,可以立即开始处理。
- 处理队列:
- 当队列不为空时:
- 从队列中取出一个顶点 u,将其加入拓扑排序结果中。
- 遍历所有邻接顶点 v,将其入度减1。
- 如果某个邻接顶点 v 的入度减至0,将其加入队列。
- 当队列不为空时:
- 检查结果:
- 如果拓扑排序结果中顶点的数量少于图中顶点的数量,说明图中存在环,无法完成拓扑排序。
注意:
队列用栈也可以,拓扑排序可能有多种结果,队列和栈得到的顺序可能不同
3.主要代码实现
1 |
|
4. 复杂度分析
- 时间复杂度:O(V + E),其中 V 是顶点数,E 是边数。每个节点和边都被处理一次。
- 空间复杂度:O(V),用于存储入度数组和队列。
三、DFS算法求解
1. 算法概述
DFS算法利用递归的方法遍历图,通过标记节点的状态来确保在拓扑排序中遵循依赖关系。该算法在遍历完一个节点及其所有邻接节点后,将该节点加入拓扑排序结果。
2. 算法步骤
- 标记状态:
- 使用一个数组或向量来标记每个节点的状态:未访问、正在访问和已完成。
- 递归处理:
- 对于每个未访问的节点,进行DFS遍历。
- 在遍历的过程中,将节点标记为“正在访问”。
- 递归访问该节点的所有邻接节点。
- 在返回时,将节点标记为“已完成”,并将其加入结果栈。
3.主要代码实现
1 |
|
4. 复杂度分析
- 时间复杂度:O(V + E),其中 V 是顶点数,E 是边数。每个节点和边都被访问一次。
- 空间复杂度:O(V),用于存储访问状态数组和栈。
拓扑排序
http://jayzencode.top/posts/fb40efc5/