Course Schedule I

There are a total of n courses you have to take, labeled from 0 to n - 1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

For example:

2, [[1,0]] There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.

2, [[1,0],[0,1]] 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.

Note: The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.

拓扑排序

复杂度

时间 O(N) 空间 O(N)

思路

先修课问题本质上是一个有向图,如果这个图无环,我们可以根据拓扑排序遍历到所有节点,如果有环则拓扑排序无法完成,遍历到的节点将少于总节点数,因为有的节点是孤岛。这题我们先根据边的关系,建一个图,并计算每个节点的入度,这里用的是数组来建图。然后从入度为0的节点,也就是入口开始广度优先搜索,按照拓扑排序的顺序遍历,最后看遍历过的节点数和总节点数的关系就行了。拓扑排序的使用方法参见外文字典

代码

public class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        ArrayList[] graph = new ArrayList[numCourses];
        int[] indegree = new int[numCourses];
        // 先初始化图,每个赋一个空列表
        for(int i = 0; i < numCourses; i++){
            graph[i] = new ArrayList<Integer>();
        }
        // 根据边建立图,并计算入度
        for(int i = 0; i < prerequisites.length; i++){
            graph[prerequisites[i][1]].add(prerequisites[i][0]);
            indegree[prerequisites[i][0]]++;
        }
        // 找到有向图的入口
        Queue<Integer> queue = new LinkedList<Integer>();
        for(int i = 0; i < indegree.length; i++){
            if(indegree[i] == 0){
                queue.add(i);
            }
        }
        // 按照拓扑排序的顺序,进行广度优先搜索
        int cnt = 0;
        while(!queue.isEmpty()){
            Integer curr = queue.poll();
            cnt++;
            ArrayList<Integer> nexts = graph[curr];
            for(int i = 0; i < nexts.size(); i++){
                int next = nexts.get(i);
                indegree[next]--;
                if(indegree[next] == 0){
                    queue.offer(next); 
                }
            }
        }
        return cnt == numCourses;
    }
}

2018/10

func canFinish(numCourses int, prerequisites [][]int) bool {
    graph := make(map[int][]int)
    indegree := make([]int, numCourses)
    // build the graph, count the indegree for each course
    for _, prerequisite := range prerequisites {
        course1 := prerequisite[0]
        course2 := prerequisite[1]
        if graph[course2] != nil {
            graph[course2] = append(graph[course2], course1)
        } else {
            graph[course2] = []int{course1}
        }
        indegree[course1]++
    }
    // find out those with 0 indegree as roots of the graph
    var queue []int
    for i, degree := range indegree {
        if degree == 0 {
            queue = append(queue, i)
        }
    }
    // traverse the graph from each root, decrement the indegree for each leaf
    count := 0
    for len(queue) > 0 {
        curr := queue[0]
        queue = queue[1:]
        count++
        for _, course := range graph[curr] {
            indegree[course]--
            // once a node's indegree is 0, it becomes a root too
            if indegree[course] == 0 {
                queue = append(queue, course)
            }
        }
    }
    // if all nodes have been the root before, we traversed the entire graph
    return count == numCourses
}

Course Schedule II

There are a total of n courses you have to take, labeled from 0 to n - 1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.

There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.

For example:

2, [[1,0]] There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1]

4, [[1,0],[2,0],[3,1],[3,2]] There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is[0,2,1,3].

拓扑排序

复杂度

时间 O(N) 空间 O(N)

思路

和I不同的是,这题要返回路径。其实也没有什么不同的,不过是多加一个数组,记录广度优先搜索的遍历顺序就行了。

注意

当没有先修课的时候,随便返回一个顺序就行了,所以初始化的时候就初始化为升序序列,方便后面直接返回。

代码

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] res = new int[numCourses];
        ArrayList<Integer>[] graph = new ArrayList[numCourses];
        int[] indegree = new int[numCourses];
        for(int i = 0; i < prerequisites.length; i++){
            if(graph[prerequisites[i][1]] == null){
                graph[prerequisites[i][1]] = new ArrayList<Integer>();
            }
            graph[prerequisites[i][1]].add(prerequisites[i][0]);
            indegree[prerequisites[i][0]]++;
        }
        Queue<Integer> queue = new LinkedList<Integer>();
        for(int i = 0; i < indegree.length; i++){
            if(indegree[i] == 0){
                queue.add(i);
            }
        }
        // 用idx记录输出数组的下标
        int idx = 0;
        while(!queue.isEmpty()){
            Integer curr = queue.poll();
            res[idx++] = curr;
            if(graph[curr] == null) continue;
            for(Integer next : graph[curr]){
                if(--indegree[next] == 0){
                    queue.offer(next); 
                }
            }
        }
        // 如果有环则返回空数组
        return idx != numCourses ? new int[0] : res;
    }
}

ethannnli
858 声望360 粉丝