题目描述(中等难度)

给定 组先修课的关系,[m,n] 代表在上 m 这门课之前必须先上 n 这门课。输出能否成功上完所有课。

解法一

把所有的关系可以看做图的边,所有的边构成了一个有向图。

对于[[1,3],[1,4],[2,4],[3,5],[3,6],[4,6]] 就可以看做下边的图,箭头指向的是需要先上的课。

想法很简单,要想上完所有的课,一定会有一些课没有先修课,比如上图的 56。然后我们可以把 56 节点删去。

207. Course Schedule - 图3

然后 34 就可以上了,同样的道理再把 34 删去。

接下来就可以去学 12 了。因此可以完成所有的课。

代码的话,用邻接表表示图。此外,我们不需要真的去删除节点,我们可以用 outNum 变量记录所有节点的先修课门数。当删除一个节点的时候,就将相应节点的先修课个数减一即可。

最后只需要判断所有的节点的先修课门数是否全部是 0 即可。

解法二

还有另一种思路,我们只需要一门课一门课的判断。

深度优先遍历 1,相当于 3 条路径

1 -> 3 -> 51 -> 3 -> 61 -> 4 -> 6

深度优先遍历 2,相当于 1 条路径

2 -> 4 -> 6

深度优先遍历 3,相当于 2 条路径

3 -> 53 -> 6

深度优先遍历 4,相当于 条路径

4 -> 6

深度优先遍历 5,相当于 1 条路径

5

6

什么情况下不能完成所有课程呢?某条路径出现了环,如下图。

207. Course Schedule - 图5

出现了 1 -> 3 -> 6 -> 3。所以不能学完所有课程。

代码的话,用邻接表表示图。通过递归实现 DFS ,用 visited 存储当前路径上的节点。

同时用 visitedFinish 表示可以学完的课程,起到优化算法的作用。

  1. public boolean canFinish(int numCourses, int[][] prerequisites) {
  2. HashMap<Integer, ArrayList<Integer>> outNodes = new HashMap<>();
  3. HashSet<Integer> set = new HashSet<>();
  4. int rows = prerequisites.length;
  5. for (int i = 0; i < rows; i++) {
  6. int key = prerequisites[i][0];
  7. int value = prerequisites[i][1];
  8. set.add(key);
  9. if (!outNodes.containsKey(key)) {
  10. outNodes.put(key, new ArrayList<>());
  11. }
  12. //存储当前节点的所有先修课程
  13. ArrayList<Integer> list = outNodes.get(key);
  14. }
  15. HashSet<Integer> visitedFinish = new HashSet<>();
  16. //判断每一门课
  17. for (int k : set) {
  18. return false;
  19. }
  20. visitedFinish.add(k);
  21. }
  22. return true;
  23. }
  24. private boolean dfs(int start, HashMap<Integer, ArrayList<Integer>> outNodes, HashSet<Integer> visited,
  25. HashSet<Integer> visitedFinish) {
  26. //已经处理过或者到了叶子节点
  27. return true;
  28. }
  29. //出现了环
  30. if (visited.contains(start)) {
  31. return false;
  32. }
  33. //将当前节点加入路径
  34. visited.add(start);
  35. ArrayList<Integer> list = outNodes.get(start);
  36. for (int k : list) {
  37. if(!dfs(k, outNodes, visited, visitedFinish)){
  38. return false;
  39. }
  40. }
  41. visited.remove(start);
  42. return true;

这道题本质上其实就是图的遍历。解法一是 BFS ,解法二是 DFS

解法一其实就是图的拓扑排序,解法二是判断图中是否有环的方法。

另外,图在代码中有两种表示形式,邻接表和邻接矩阵,上边的解法都采用的是邻接表。

添加好友一起进步~

如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 这里 查看详情