【路飞】207. 课程表、210. 课程表 II

154 阅读1分钟

207. 课程表

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

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

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

示例 1:

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

示例 2:

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

解题思路:这题的解题思路是利用拓扑排序法,可以将课程之间的关系抽象化成一张图,然后找出每张节点的度数量,当度数量为空的时候就入栈,然后出栈鄂时候,将有关系的度数量减一,代码如下:

var canFinish = function(numCourses, prerequisites) {
    if(prerequisites.length <= 1) return true;
    //记录每个课程必须学习的课程数量,比如想修1,必须先修0和2,idx = {1:2},这个就是度数量,只有度数量为0这个课程才能修;
    let idx = {};
    let idxList = {};//记录每个课程必须学习有哪些,比如idxList = {1:[0,2]}
    let temp = []//记录度数量为0的数组
    for(let i = 0 ; i < prerequisites.length; i ++){
        let [a,b] = prerequisites[i];
        if(!idx[a]) idx[a] = 0;
        //课程a度数量加一
        idx[a] ++;
        if(!idxList[b])  idxList[b] = [];
        //记录b课程有关系的课程,当b出栈的时候,有关系的课程度数量减一
        idxList[b].push(a);
    }
    //将度数量为空的入栈,也就是起始点位置
    for(let i = 0; i < numCourses; i ++){
        if(!idx[i] || idx[i] == 0) temp.push(i); 
    }
    let cnt = 0;
    while(temp.length > 0){
        let x = temp[0];
        temp.shift(),cnt ++;
        let j = idxList[x] ? idxList[x] : [];
        //根据出栈的信息将相关联的课程度数量减一,如果为0就入栈
        for(let i = 0; i < j.length; i ++){
            idx[j[i]] --;
            if(idx[j[i]] == 0) temp.push(j[i]);
        }
    }
    //出栈的课程也就是能修的课程,相等的话就说明能完成所有课程
    return cnt == numCourses;
};

210. 课程表 II

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。 返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:[0,1]
解释:总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。

示例 2:

输入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
输出:[0,2,1,3]
解释:总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。

示例 3:

输入: numCourses = 1, prerequisites = []
输出: [0]

解题思路:这题的要求跟课程表一基本类似,上一题是判断是否能修完,这题是要求输出上课的顺序,其实每次出栈的课程就是已经修完的课程,代码如下:

var findOrder = function(numCourses, prerequisites) {
     //记录每个课程必须学习的课程数量,比如想修1,必须先修0和2,idx = {1:2},这个就是度数量,只有度数量为0这个课程才能修;
    let idx = {};
    let idxList = {};//记录每个课程必须学习有哪些,比如idxList = {1:[0,2]}
    let temp = []//记录度数量为0的数组
    for(let i = 0; i < prerequisites.length; i ++){
        let [a,b] = prerequisites[i];
        if(!idx[a]) idx[a] = 0;
        //记录a的所有度数量
        idx[a] ++;
        if(!idxList[b]) idxList[b] = [];
        //记录与b有关系的课程
        idxList[b].push(a); 
    }
    //度数量为空的入栈,也就是其实位置
    for(let i = 0; i < numCourses; i ++){
        if(!idx[i] || idx[i] == 0) temp.push(i);
    }
    //遍历关系,将有关系课程处理掉
    let cnt = [];
    while(temp.length > 0){
        let x = temp[0];
        temp.shift();
        cnt.push(x);
        let p = idxList[x] ? idxList[x] : [];
        for(let i = 0; i < p.length; i ++){
            idx[p[i]] --;
            if(idx[p[i]] == 0) temp.push(p[i]);
        }
    }
    //当修完的课程等于课程数量就说明是可以修完的,如果不相等就说明关系不存在
    return cnt.length == numCourses ? cnt : [];
};