携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
题目(Course Schedule)
链接:leetcode-cn.com/problems/co… 解决数:1593 通过率:53.9% 标签:深度优先搜索 广度优先搜索 图 拓扑排序 相关公司:amazon microsoft facebook 你这个学期必须选修 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 。这是不可能的。
提示:
1 <= numCourses <= 105 0 <= prerequisites.length <= 5000 prerequisites[i].length == 2 0 <= ai, bi < numCourses prerequisites[i] 中的所有课程对 互不相同
思路
- 邻接表
h表示学习某课程的需要课程。哈希表r表示状态1搜索中2已完成undefined未搜索 - 邻接表
h存学习某课程所需课程,哈希表r存状态1搜索中2已完成undefined未搜索 - 课程总数数组与
邻接表中存在的课程数组取差集 - 遍历
邻接表,从任意课程递归,遇课程看状态:搜索中, 存在环,终止递归,返回false已完成,不做任何事,终止递归,返回true未搜索,搜索该课程所需课程- 课程无
所需课程,标记已完成,已完成课程数+1结果集长度 =课程总数,终止地址,返回false
- 课程有
所需课程,标记搜索中,遍历搜索所需课程回溯所有所需课程都为已完成,该课程标记已完成,已完成课程数+1
- 课程无
- 已学习课程 === 课程总数,返回
false,终止递归。因为搜索中也会返回false,需再判断一次
代码
var canFinish = function(numCourses, prerequisites, h = Object.create(null), r = Object.create(null), n = 0) {
prerequisites.forEach(v => h[v[0]] ? h[v[0]].push(v[1]) : h[v[0]] = [v[1]])
var f = v => (r[v] = 2, ++n < numCourses),
d = v => r[v] === 1 ? false : r[v] || (h[v] ? (r[v] = 1, h[v].every(v => d(v)) && f(v)) : f(v))
for (var k in h) if(d(k) === false) return n === numCourses
return true
};
深度优先搜索 · 剪枝
解题思路
- 邻接表
h表示学习某课程的需要课程。哈希表r记录搜索经历的课程 - 找到
没有需要课程的课程,将r中课程放入m表示这些课程已完成(有办法学) - 继续
递归,跳过已完成的课程。最后剩下没办法学(存在依赖环)的课程加不到m中
代码
var canFinish = function(numCourses, prerequisites, h = {}, m = []) {
prerequisites.forEach(v => h[v[0]] ? h[v[0]].push(v[1]) : h[v[0]] = [v[1]])
var d = (v, r) => r.includes(v) ? false : (m.includes(v) || h[v] === undefined) ?
(m = m.concat(r), m.length === numCourses ? false : true) :
(r.push(v), h[v].every(v => d(v, r.slice())))
for (var k in h) if(d(Number(k), []) === false) return m.length === numCourses
return true
};
广度优先搜索
解题思路
- 双
邻接表:h表示学习某课程的需要课程,r表示完成某课程的可选课程 - 根据
h和课程总数,找出不需要任何课程的课程,放入可学习队列q - 根据
r,遍历完成某课程后的可选课程,根据h- 该
可选课程只需要完成某课程,该可选课程放入可学习队列q并从h中删除 - 该
可选课程还需完成其它课程,该可选课程需要课程中把某课程删除
- 该
代码
完整版
var canFinish = function(numCourses, prerequisites, h = Object.create(null), r = Object.create(null)) {
prerequisites.forEach(v => (h[v[0]] ? h[v[0]].push(v[1]) : h[v[0]] = [v[1]], r[v[1]] ? r[v[1]].push(v[0]) : r[v[1]] = [v[0]]))
for (var i = 0, q = [], n; i < numCourses; i++) !h[i] && q.push(i)
while (q.length)
r[n = q.pop()] && r[n].forEach(v => {
if (h[v].length === 1) {
q.push(v)
delete h[v]
} else {
h[v].splice(h[v].findIndex(v=> v === n), 1)
}
})
return Object.keys(h).length === 0
};