拓扑排序topological sort

104 阅读1分钟

拓扑排序经典问题 - 课程表:

207. Course Schedule

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai.

  • For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1.

Return true if you can finish all courses. Otherwise, return false.

 

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take. 
To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take. 
To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

两种解法:

  1. 先建立adjancency list,然后判断是否有环
    这个方法比较straightforward,通过递归判断是否有环
class Solution {
    public boolean findCircle(int i, List<List<Integer>> courseList, int[] visited) {
        if(visited[i] == 1) return true;
        if(visited[i] == 2) return false;
        // 总结起来,if(visited[i] == 2) return false; 这一行代码的目的是在深度优先搜索过程中,如果遇到已经完全探索过的节点,立即返回 false
        
        visited[i] = 1;
        for(int next: courseList.get(i)) {
            if(findCircle(next, courseList, visited)) {
                return true;
            }
        }
        visited[i] = 2;
        return false;

    }
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<List<Integer>> courseList = new ArrayList<>(numCourses);
        for(int i = 0; i < numCourses; i++) {
            courseList.add(new ArrayList<>());
        }

        for(int[] pre: prerequisites) {
            courseList.get(pre[1]).add(pre[0]);
        }

        int[] visited = new int[numCourses];
        for(int i = 0; i < numCourses; i++) {
            if(findCircle(i, courseList, visited)) {
                return false;
            }
        }
        return true;
    }
}
  1. 通过拓扑排序,限时找到所有课程的入度,这需要一个array来辅助,然后建立一个queue,先从无入度的顶点开始遍历,然后将所有以该门课为入度的起点的课的入度减一,最后有一个count判断所有入度的节点,如果等于numCourses则说明结果正常,否则返回空数组
class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        int[] indegree = new int[numCourses]; // indegree指的是当前的课有多少prerequisite

        for(int i = 0; i < numCourses; i++) {
            map.put(i, new ArrayList<>());
        }

        for(int[] pre: prerequisites) {
            map.get(pre[1]).add(pre[0]);
            indegree[pre[0]]++;
        }
        Deque<Integer> queue = new ArrayDeque<>();
        for(int i = 0; i < numCourses; i++) {
            if(indegree[i] == 0) {
                queue.offer(i);
            }
        }

        int[] res = new int[numCourses];
        int index = 0;
        while(!queue.isEmpty()) {
            int curr = queue.poll();
            if (index < numCourses) {
                res[index] = curr;
            }
            index++;
            for(int next: map.get(curr)) {
                indegree[next]--;
                if(indegree[next] == 0) {
                    queue.offer(next);
                }
            }
        }

        return index == numCourses ? res : new int[0];
    }
}

210题和207题差不多,不做考虑