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 : [];
};