拓扑排序
介绍
拓扑排序可以将一个有向无环图(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 4.0 许可协议。转载请注明来自 immix's blog!
评论





