leetcode 207 课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
拓扑排序
这种在一个图中,互相有依赖关系,需要按照依赖顺序排序输出的问题,就是拓扑排序。拓扑排序除了排序输出之外,还可以验证图中是否有环。比如下面常见的拓扑排序的应用。
拓扑排序的方法
一是 Kahn 算法。二是深度优先遍历
Kahn 算法
个人认为 Kahn 算法还是比较好理解的。首先需要定义 b 依赖与 a,a 要先与 b 执行,那么就是 a->b。
此时 b 的入度是 1,a 的入度是 0。
- 算法的思路就是首先统计出所有的点的入度,还需要统计所有点的 next 点
- 然后找到入度为 0 的点,访问它,随后,将该点的所有 next 点的入度都减 1
- 然后重复 2 这个步骤
- 直到没有入度为 0 的点为止
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| function canFinish(numCourses: number, prerequisites: number[][]): boolean { const inDegree = Array(numCourses).fill(0); const adjacent = inDegree.map(() => []);
for (const [cur, pre] of prerequisites) { inDegree[cur] += 1; adjacent[pre].push(cur); }
const queue = []; for (let i = 0; i < inDegree.length; i++) { if (inDegree[i] === 0) { queue.push(i); } }
const list = []; while (queue.length) { const cur = queue.shift(); list.push(cur);
adjacent[cur].forEach((next) => { inDegree[next]--; if (inDegree[next] === 0) { queue.push(next); } }); }
return list.length === numCourses; }
|
dfs 深度优先遍历
深度优先遍历,也是解决拓扑排序的常用办法。其思路就是沿着一个点,一直往前找它的前置点,直到没有前置的点了,就是找到头了。此时就可以回溯访问了(从外到内探索,从内到外回溯访问)。
需要注意的是,需要记录点的状态,未访问|访问中|已经访问。
- 如果未访问需要标记未访问中
- 如果是访问中,则说明又访问了一次,存在环
- 如果是已经访问,那么直接返回就可以了;比如(这里和 Kahn 算法是反的,a->b 代表 b 先于 a 执行。a 依赖 b)
- a->b->c
- d->b->c
- 访问完 a 这条链路,b 和 c 就已经访问了。因此再访问 d 这条链路时,就不需要访问 b 和 c 了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function dfs( cur: number, pres: number[][], visited: number[], res: number[] ): boolean { if (visited[cur] === 2) { return true; } else if (visited[cur] === 1) { return false; } else { visited[cur] = 1; for (let i = 0; i < pres[cur].length; i++) { if (!dfs(pres[cur][i], pres, visited, res)) { return false; } } visited[cur] = 2; res.push(cur); return true; } }
function findOrder(numCourses: number, prerequisites: number[][]): number[] { const visited = Array(numCourses).fill(0); const pres = visited.map(() => []);
for (let [cur, pre] of prerequisites) { pres[cur].push(pre); } const res = []; for (let i = 0; i < numCourses; i++) { if (!dfs(i, pres, visited, res)) { return []; } } return res; }
|