开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情
题目:LeetCode
现在你总共有 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]
提示:
1 <= numCourses <= 20000 <= prerequisites.length <= numCourses * (numCourses - 1)prerequisites[i].length == 20 <= ai, bi < numCoursesai != bi- 所有
[ai, bi]互不相同
解题思路
拓扑排序是一种有向无环图,DFS和BFS通过一些修改都可以用来判断环,不过对于拓扑排序最经典的还是使用BFS的方式构建入度表,来依次遍历各节点,如果最后遍历出来还存在节点入度不为0的节点,也就是遍历不完全则说明一定存在环。 算法步骤:
- 根据给定的节点连接关系,构建图的邻接表
- 构建每个节点的入度和出度表
- 将入度为0的节点存入队列
- 依次弹出遍历队列中的节点,并更新该节点相邻节点的入度,若出现新增的入度为0的节点则加入队列
- 记录访问节点的个数cnt,判断节点个数cnt是否与numCourses一致,否则说明该图存在环,无法完成拓扑排序
代码实现
public int[] findOrder(int numCourses, int[][] prerequisites) {
HashMap < Integer, ArrayList < Integer >> nodemaplist = new HashMap < > ();
int[] res = new int[numCourses];
if (numCourses == 0) //特判
return res;
else if (numCourses == 1) {
res[0] = 0;
return res;
}
int[] enter = new int[numCourses]; //入度
int[] to = new int[numCourses]; //出度
for (int[] courserelation: prerequisites) {
int target = courserelation[0];
int pre = courserelation[1];
enter[target] ++;
to[pre] ++;
if (!nodemaplist.containsKey(pre)) //对先驱节点创建邻接表
nodemaplist.put(pre, new ArrayList < Integer > ());
nodemaplist.get(pre).add(target);
}
Queue < Integer > queue = new LinkedList < > (); //用队列来维护bfs遍历
int cnt = 0; //记录访问的节点数
for (int i = 0; i < enter.length; i++) {
if (enter[i] == 0)
queue.offer(i); //初始化队列,将queue加入其中
}
while (!queue.isEmpty()) {
int temp = queue.remove();
res[cnt++] = temp;
if (to[temp] == 0) //出度为0则不存在从该节点发出的边,无需遍历
continue;
for (int target: nodemaplist.get(temp)) {
enter[target] --;
if (enter[target] == 0)
queue.offer(target);
}
}
if (cnt < numCourses) //cnt比numCousers小则遍历不完全,一定存在环
return new int[0];
else
return res;
}
运行结果
复杂度分析
- 空间复杂度:
- 时间复杂度:
在掘金(JUEJIN) 一起分享知识, Keep Learning!