207. Course Schedule [Medium]

本文探讨了如何通过BFS(广度优先搜索)和DFS(深度优先搜索)算法判断有向图是否存在环,并利用这两种方法实现拓扑排序。两种方法对比,分别涉及邻接表构建、入度矩阵和访问矩阵的使用,以及在竞赛题目802.FindEventualSafeStates中的应用。

有向图是否有环拓扑排序

该题的本质是检查有向图是否有环,只要无环肯定能上完课程

【两种思路】

BFS思路:先计算邻接表、入度矩阵,然后用一个queue进行拓扑排序

DFS思路:先计算邻接表,用dfs判断每一个节点出发是否有环,用Boolean类型的visited矩阵记录是否访问、是否有环的情况

和802. Find Eventual Safe States [Medium]几乎一模一样https://blog.csdn.net/Squirrelmi/article/details/116307963,只是802直接给出了邻接表,本题是给出有向边,还需要自己建立邻接表

【BFS vs DFS】

都需要建立邻接表

BFS要建立入度矩阵int[] indegrees,要用queue

DFS要建立访问矩阵Boolean[] visited,要递归

/**
 * BFS拓扑排序:
 * 先建立和计算邻接表、入度矩阵,然后用一个queue进行拓扑排序
 * 每删除一个队列中的节点,将numCourses--,并将与其临接的所有节点入度减一,将入度变为0的节点加入队列
 * 队列为空时循环结束,numCourses==0说明能上完所有课,否则不能
 * Runtime: 3 ms, faster than 85.93%
 * Memory Usage: 39.8 MB, less than 38.80%
 */
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        // 拓扑排序准备工作:建立和计算临接表、入度矩阵
        List<Integer>[] adjacency = new List[numCourses]; // 邻接表
        int[] indegrees = new int[numCourses]; // 入度矩阵
        for (int i = 0; i < numCourses; i++) // 为邻接表的每一行开辟空间
            adjacency[i] = new ArrayList<>();
        for (int[] pair : prerequisites) { // 计算邻接表和入度矩阵
            indegrees[pair[0]]++;
            adjacency[pair[1]].add(pair[0]);
        }
        // 开始拓扑排序
        Deque<Integer> q = new LinkedList<>(); // 用于拓扑排序的队列
        for (int i = 0; i < numCourses; i++) {
            if (indegrees[i] == 0) {
                q.offer(i);
            }
        }
        while (!q.isEmpty()) {
            int curr = q.poll();
            numCourses--;
            for (int i : adjacency[curr]) {
                if (--indegrees[i] == 0) {
                    q.offer(i);
                }
            }
        }
        return numCourses == 0;
    }
}
/**
 * DFS思路,检查无环
 * Runtime: 2 ms, faster than 99.01%
 * Memory Usage: 39.4 MB, less than 65.06%
 */
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        // 计算邻接表
        List<Integer>[] adjacency = new List[numCourses];
        for (int i = 0; i < numCourses; i++) {
            adjacency[i] = new ArrayList<>();
        }
        for (int[] pair : prerequisites) {
            adjacency[pair[1]].add(pair[0]);
        }
        // 开始DFS判断无环
        Boolean[] visited = new Boolean[numCourses];
        for (int i = 0; i < numCourses; i++) {
            if (!helper(adjacency, i, visited)) {
                return false;
            }
        }
        return true;
    }

    private boolean helper(List<Integer>[] adjacency, int i, Boolean[] visited) { // 检查是否没有环
        if (visited[i] != null) {
            return visited[i];
        }
        visited[i] = false;
        for (int next : adjacency[i]) {
            if (!helper(adjacency, next, visited)) {
                return false;
            }
        }
        visited[i] = true;
        return true;
    }
}

第一次复习

还是用了DFS和BFS两种方法,DFS写的和之前一模一样,BFS这次多一点注释可能好理解一些:

/**
 * BFS(拓扑排序)
 * 构造临接表和入度矩阵,并用一个queue进行拓扑排序
 * 每次把当前能上的课(入度为0,即所有前置条件都达到了的)都上完,并解除它们后置节点的前置条件(入度--)
 */
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        if (prerequisites.length == 0 || prerequisites[0].length == 0) {
            return true;
        }

        List<Integer>[] adjacency = new List[numCourses];
        int[] indegree = new int[numCourses];
        LinkedList<Integer> queue = new LinkedList();
        
        // initiate adjacency table, indegree array, and queue
        for (int i = 0; i < numCourses; i++) {
            adjacency[i] = new LinkedList();
        }
        for (int[] pair : prerequisites) {
            adjacency[pair[0]].add(pair[1]);
            indegree[pair[1]]++;
        }
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0) {
                queue.offer(i);
            }
        }
        
        // topological sort
        while (!queue.isEmpty()) {
            int curr = queue.poll(); // finish the course numbered "curr"
            numCourses--;
            for (int next : adjacency[curr]) {
                if (--indegree[next] == 0) {
                    queue.offer(next);
                }
            }
        }
        
        return numCourses == 0; // now we have finished all courses that can be finished, if there are still unfinished courses, the result is false
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值