207. Course Schedule (M)

Course Schedule (M)

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?

Example 1:

Input: 2, [[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: 2, [[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.

Note:

  1. The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
  2. You may assume that there are no duplicate edges in the input prerequisites.

题意

假设选课时有一些课程有前置课程要求,给定一系列课程及其所有的前置课程,判断能否上完所有课。

思路

很典型的拓扑排序问题,经典解法有 BFSDFS 两种。当图中存在环时则没有办法上完所有课。


代码实现 - BFS

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] inDegree = new int[numCourses];				// 入度数组
        List<List<Integer>> edge = new ArrayList<>();		// 记录图信息
        // 初始化邻接表
        for (int i = 0; i < numCourses; i++) {
            edge.add(new ArrayList<>());
        }
        // 生成图并记录初始入度信息
        for (int i = 0; i < prerequisites.length; i++) {
            inDegree[prerequisites[i][0]]++;
            edge.get(prerequisites[i][1]).add(prerequisites[i][0]);
        }
        // 将所有入度为0的结点入队
        Queue<Integer> q = new ArrayDeque<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                q.offer(i);
            }
        }
        // 更新结点入度和队列,并记录拓扑序列中结点的个数
        int count = 0;
        while (!q.isEmpty()) {
            int u = q.poll();
            count++;
            for (int i = 0; i < edge.get(u).size(); i++) {
                int v = (int) edge.get(u).get(i);
                inDegree[v]--;
                if (inDegree[v] == 0) {
                    q.offer(v);
                }
            }
        }
        // 如果有结点不在拓扑序列中,说明图存在环
        return count == numCourses;
    }
}

代码实现 - DFS

// DFS方法实际上就是判断是否为有向无环图
class Solution {
    private boolean[] visit;				// 记录结点是否被访问
    private boolean[] inStack;				// 记录结点是否还在递归栈中
    private List<List<Integer>> edge;		// 记录图

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        visit = new boolean[numCourses];
        inStack = new boolean[numCourses];
        edge = new ArrayList<>();
        // 初始化邻接表
        for (int i = 0; i < numCourses; i++) {
            edge.add(new ArrayList<>());
        }
        // 生成图
        for (int i = 0; i < prerequisites.length; i++) {
            edge.get(prerequisites[i][1]).add(prerequisites[i][0]);
        }
        // DFS处理
        for (int i = 0; i < numCourses; i++) {
            if (!visit[i]) {
                boolean flag = dfs(i);
                if (!flag) return false;		// 存在环则不可能有拓扑序列
            }
        }
        return true;
    }

    private boolean dfs(int u) {
        visit[u] = true;
        inStack[u] = true;
        for (int v : edge.get(u)) {
            if (!visit[v]) {
                boolean flag = dfs(v);
                if (!flag) return false;
            } else if (inStack[v]) {
                return false;		// 如果下一条边连接的结点还在递归栈中,说明存在环
            }
        }
        inStack[u] = false;			// 所有出边处理完后,将当前结点排除在递归栈外
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值