拓扑排序一般用来解决求一个有先后关系的排序问题。如果在一连串事件中,有着必要的先后关系或依赖关系,就可以抽象成图中的拓扑排序。
出度和入度
拓扑排序需要用到出度和入度的概念。
出度:以该点为起点的边的数量
入度:以该点为终点的边的数量
如果把图中每个结点看作一个事件,边看作一种依赖关系。那么一个点的入度就是该点所需的先决条件数量,一个点的出度就是 以该点作为其他点先决条件 的 点的数量。显然,入度为0的点,就是序列的起始点。
显然,一个图可以进行拓扑排序的充要条件是他是一个有向无环图(DAG)
BFS进行拓扑排序
bfs下的拓扑排序有两种思路:
无前驱优先型
无后继优先型
无前驱优先型的算法思路是:先找出图中入度为0的点(没有前驱)加入队列,然后将该点所有的后继点的入度减一(相当与把加入队列的点从图中删除)。
然后重复这一步骤,直到所有点加入队列,则完成拓扑排序。
无后继优先型的思路与无前驱型相同,不过在队列中的结点顺序的逆序才是它的拓扑排序。
无法完成拓扑排序(不是DAG)的情况:如果图中不存在入度(或出度)为0的点,但仍有点未加入队列,证明它不是一个有向无环图,没有拓扑排序。
DFS进行拓扑排序
dfs天然适合拓扑排序,因为它的访问过程本身就需要(在一定程度上)按照拓扑排序。
具体思路就是,先找到一个入度为0的点,对其进行dfs,回溯时的顺序就是就是拓扑排序的逆序。
有以下几个要注意的点:
每次访问的结点必须入度为零
不能出现回退边(访问到一个在前边的递归过程中没有处理完的点),出现回退边意味着这个图不是一个有向无环图
按最小的字典序输出拓扑排序
一个图的拓扑排序往往不只有一种,所以有时题目要求输出字典序最小的拓扑排序,这时我们只需要把bfs中的队列换成优先队列就好,同时入度为0的点字典序小的先出队。
(这时不能用dfs的方法,因为dfs是按层搜索,无法区分同一层结点的优先级)
几道练手水题
to be continued......