95 lines
3.0 KiB
JavaScript
95 lines
3.0 KiB
JavaScript
/**
|
||
* 课程安排 II - 拓扑排序(DFS)
|
||
* @param {number} numCourses - 课程总数
|
||
* @param {number[][]} prerequisites - 每一对 [a, b] 表示上课程 a 需要先完成课程 b
|
||
* @return {number[]} - 返回一个可行的课程学习顺序,如果无法完成所有课程则返回 []
|
||
*/
|
||
const findOrder = function (numCourses, prerequisites) {
|
||
return dfsTopoSort(numCourses, prerequisites);
|
||
};
|
||
|
||
/**
|
||
* 使用 DFS 实现拓扑排序,并判断是否存在环
|
||
* @param {number} numCourses
|
||
* @param {number[][]} prerequisites
|
||
* @return {number[]}
|
||
*/
|
||
function dfsTopoSort(numCourses, prerequisites) {
|
||
const graph = Array.from({ length: numCourses }, () => []); // 邻接表表示图
|
||
const visited = Array(numCourses).fill(0); // 节点状态:0=未访问,1=访问中,2=已完成
|
||
const result = []; // 用于存放拓扑排序结果(逆序)
|
||
let hasCycle = false; // 用于标记图中是否存在环
|
||
|
||
// 构建图:b -> a 表示先学 b 才能学 a
|
||
for (const [a, b] of prerequisites) {
|
||
graph[b].push(a);
|
||
}
|
||
|
||
/**
|
||
* 深度优先搜索当前节点
|
||
* @param {number} node
|
||
*/
|
||
function dfs(node) {
|
||
if (visited[node] === 1) {
|
||
// 当前节点正在访问中,说明存在环
|
||
hasCycle = true;
|
||
return;
|
||
}
|
||
if (visited[node] === 2 || hasCycle) {
|
||
// 节点已处理,或已发现环,直接返回
|
||
return;
|
||
}
|
||
|
||
visited[node] = 1; // 标记为访问中
|
||
for (const neighbor of graph[node]) {
|
||
dfs(neighbor);
|
||
}
|
||
visited[node] = 2; // 标记为已完成
|
||
result.push(node); // 所有邻居处理完后再加入结果,确保当前节点排在后面
|
||
}
|
||
|
||
// 遍历所有节点,防止图不连通
|
||
for (let i = 0; i < numCourses; i++) {
|
||
dfs(i);
|
||
}
|
||
|
||
// 如果存在环,则无法完成所有课程
|
||
return hasCycle ? [] : result.reverse();
|
||
}
|
||
|
||
/*
|
||
使用kahn算法来实现,一个顶点能从其他顶点过来表明有一个入度,有多少个顶点能到达这个顶点就有多少个入度,这里直接copy207
|
||
修改一下即可
|
||
*/
|
||
function f2(numCourses, prerequisites) {
|
||
const graph = Array.from({ length: numCourses }, () => []);
|
||
const inDegree = Array(numCourses).fill(0);
|
||
const result = [];
|
||
|
||
// 构建图和入度表
|
||
for (const [a, b] of prerequisites) {
|
||
graph[b].push(a);
|
||
inDegree[a]++;
|
||
}
|
||
|
||
// 找出入度为零的顶点,将其加入队列
|
||
const queue = [];
|
||
for (let i = 0; i < numCourses; i++) {
|
||
if (inDegree[i] === 0) queue.push(i);
|
||
}
|
||
|
||
// 从队列中输出所有入度为零的顶点,如果输出的顶点数量和课程数量一致,表明可以拓扑排序,否则有环
|
||
while (queue.length > 0) {
|
||
const node = queue.shift();
|
||
// 将入度为零的顶点存入结果集合
|
||
result.push(node);
|
||
for (const neighbor of graph[node]) {
|
||
inDegree[neighbor]--;
|
||
if (inDegree[neighbor] === 0) queue.push(neighbor);
|
||
}
|
||
}
|
||
|
||
// 如果结果集中的顶点的数量小于numCourses那么表明有环,放回[],否则返回结果集
|
||
return result.length < numCourses ? [] : result;
|
||
}
|