拓扑排序

2020/07/01

「拓扑排序」是专门应用于有向图的算法,它能够顺带检测有向图中是否存在环。注意:「拓扑排序」的结果不唯一。 如果且仅当图形没有定向循环,即如果它是有向无环图(DAG),则拓扑排序是可能的。任何 DAG 具有至少一个拓扑排序,存在算法用于在线性时间内构建任何 DAG 的拓扑排序。

对于拓扑排序类型的问题,可以采用BFS和DFS来解决。BFS的解法很经典,本文只介绍BFS,不对DFS进行扩展(其实是我对拓扑排序的DFS解决不太熟~~~)。

以leetcode 207 课程表问题为例,算法流程分为以下步骤:

  • 1、使用邻接表保存有向图
  • 2、统计图中每个节点的入度,生成入度数组
  • 3、使用队列存储所有入度为0的节点
  • 4、当队列非空时,依次将队首节点出队,并更新该节点对应的邻接表,将其邻接表上的节点的入度-1,同时将新的入度为0的节点入队
  • 5、如果整个图是有向无环图,那么所有节点可以完成拓扑排序(即所有节点入队并出队),否则,最终将有节点的入度不为0,不能完成拓扑排序

时间复杂度 O(N + M): 遍历一个图需要访问所有节点和所有临边,N 和 M 分别为节点数量和临边数量; 空间复杂度 O(N + M): 为建立邻接表所需额外空间。

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] in = new int[numCourses];
        List<List<Integer>> list = new ArrayList<>();
        for (int i=0; i<numCourses; i++) {
            list.add(new ArrayList<>());
        }
        for (int[] prerequisite: prerequisites){
            in[prerequisite[0]]++;
            list.get(prerequisite[1]).add(prerequisite[0]);
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i=0; i<numCourses; i++) {
            if (in[i] == 0) {
                queue.offer(i);
            }
        }
        int count = 0;
        while (!queue.isEmpty()) {
            count++;
            int temp = queue.poll();
            for (int i : list.get(temp)) {
                in[i]--;
                if (in[i] == 0) {
                    queue.offer(i);
                }
            }
        }
        return count == numCourses;
    }
}

Post Directory