/** * 课程安排 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; }