Leetcode --- 课程表问题(拓扑排序)

2021-05-03  本文已影响0人  _code_x

写在前:拓扑排序本质是BFS和贪心算法,是这两种算法在有向图应用的专有名词,即拓扑排序针对有向图问题。参考这里

ps:入度:指向当前节点的节点个数;后继节点:当前节点指向的节点

拓扑排序注意点:

算法的基本流程

代码具体实现的时候,除了保存入度为 0 的队列,我们还需要两个辅助的数据结构(本质都是数组结构)

这个两个数据结构在遍历题目给出的邻边以后就可以很方便地得到。

应用场景:任务调度计划、课程安排

1.课程表(207 - 中)

题目描述:你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

示例:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的

代码实现

public boolean canFinish(int numCourses, int[][] prerequisites) {
    if (numCourses <= 0) return false;
    // 不需要先修课程
    if (prerequisites.length == 0) return true;

    int[] inDegree = new int[numCourses];
    HashSet<Integer>[] adj = new HashSet[numCourses];
    for (int i = 0; i < numCourses; i++) {
        adj[i] = new HashSet<>();
    }
    // 遍历邻边,得到入度数组和邻接表
    for (int[] p : prerequisites) {
        inDegree[p[0]]++;
        adj[p[1]].add(p[0]);
    }

    Queue<Integer> queue = new LinkedList<>();

    // 加入入度为0的节点
    for (int i = 0; i < numCourses; i++) {
        if (inDegree[i] == 0) {
            queue.add(i);
        }
    }

    // 记录已经出队的课程数量(选修的课程数)
    int cnt = 0;
    while (!queue.isEmpty()) {
        Integer top = queue.poll();
        cnt++;
        // 遍历当前节点的所有后继节点
        for (int successor : adj[top]) {
            // ps:通过重置后继几点的入度数组,“删除”top节点
            inDegree[successor]--;
            if (inDegree[successor] == 0) {
                queue.add(successor);
            }
        }
    }
    return cnt == numCourses;
}

2.课程表II(210 - 中)

题目描述:现在你总共有 n 门课需要选,记为 0 到 n-1。在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。

可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。

示例:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的

代码实现

public int[] findOrder(int numCourses, int[][] prerequisites) {
    if (numCourses <= 0) return new int[0];
    int[] inDegree = new int[numCourses];
    HashSet<Integer>[] adj = new HashSet[numCourses];

    for (int i = 0; i < numCourses; i++) {
        adj[i] = new HashSet<>();
    }

    for (int[] p : prerequisites) {
        inDegree[p[0]]++;
        adj[p[1]].add(p[0]);
    }

    int cnt = 0;
    int[] ans = new int[numCourses];
    Queue<Integer> queue = new LinkedList<>();

    // 入度为0的节点入队列
    for (int i = 0; i < numCourses; i++) {
        if (inDegree[i] == 0) {
            queue.add(i);
        }
    }

    // 当前课程修完,并加入结果集
    while (!queue.isEmpty()) {
        Integer top = queue.poll();
        ans[cnt] = top;
        cnt++;

        for (int successor : adj[top]) {
            inDegree[successor]--;
            if (inDegree[successor] == 0) {
                queue.add(successor);
            }
        }
    } 
    if (cnt == numCourses) return ans;
    return new int[0]; 
}
上一篇 下一篇

猜你喜欢

热点阅读