拓扑排序
介绍
拓扑排序可以将一个有向无环图(DAG)的节点进行排序,排序后即可依次进行处理,使得排在前面的节点不能依赖于排在后面的节点,这样就可以满足 DP 的无后效性,进行 DP 求解某些问题。
Kahn 算法
Kahn 算法的步骤如下:
- 从图中入度为 0 的节点的集合中任取一个,并加入拓扑序;
- 删除该顶点和所有与其相连边;
- 重复 1-2 步,直到图中没有入度为 0 的节点。若此时图中还有边,说明图中有环。
如以下伪代码:
\begin{algorithm} \caption{Kahn's Algorithm} \begin{algorithmic} \STATE $ L \leftarrow $ Empty list that will contain the sorted elements \STATE $ S \leftarrow $ Set of all nodes with no incoming edge \WHILE{$S$ \textrm{is not empty}} \STATE remove a node $n$ from $S$ \STATE add $n$ to $L$ \FOR {each node $m$ with an edge $e$ from $n$ to $m$} \STATE remove edge $e$ from the \textit{graph} \IF{if $m$ has no other incoming edges} \STATE insert $m$ into $S$ \ENDIF \ENDFOR \ENDWHILE \IF {\textit{graph} has edges} \STATE return error (\textit{graph} has at least one cycle) \ELSE \STATE return $L$ (a topologically sorted order) \ENDIF \end{algorithmic} \end{algorithm}
以下是算法的一个示例:
在实现过程中,一般用一个队列维护入度为 0 的节点,并使用数组维护每个点的入度。在删边时,无需真正从图中删边,只需减小该点的入度即可。若入度减小为 0,则将其加入队列。在 DAG 上,这样一定能不重不漏的遍历每个节点(想一想,为什么)。最后只需判断是否从集合中取出了 个节点即可判断是否存在拓扑排序。
实现如下:
1 |
|
时间复杂度 。
DFS 算法
DFS 算法中,对于节点 ,只有在 的所有后继加入拓扑排序后, 才能加入拓扑排序,来实现了拓扑排序。具体来说,就是在 DFS 过程的回溯中加入拓扑排序。
伪代码如下:
\begin{algorithm} \caption{DFS Algorithm} \begin{algorithmic} \STATE $L \leftarrow$ Empty list that will contain the sorted nodes \WHILE{exists nodes without a permanent mark} \STATE select an unmarked node n \STATE \CALL{dfs}{n} \ENDWHILE \FUNCTION{dfs}{node $n$} \IF{$n$ has a permanent mark} \STATE return \ENDIF \IF{$n$ has a temporary mark} \STATE stop (graph has at least one cycle) \ENDIF \STATE mark $n$ with a temporary mark \FOR{each node $m$ with an edge from $n$ to $m$} \STATE \CALL{dfs}{m} \ENDFOR \STATE mark $n$ with a permanent mark \STATE add $n$ to head of $L$ \ENDFUNCTION \end{algorithmic} \end{algorithm}
1 |
|
拓扑排序常见问题
是否存在拓扑排序
在 Kahn 算法中,只需判断是否执行了 次循环即可。
拓扑排序是否唯一
只需保证在任何时刻,Kahn 算法中集合元素不超过一个,拓扑排序则唯一,反之不唯一。
求字典序最小的拓扑排序
将 Kahn 算法中队列替换成优先队列即可。复杂度 。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 immix's blog!
评论