[LeeCode] 207. Course Schedule Java
题目:
There are a total of n courses you have to take, labeled from 0
to n - 1
.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
For example:
2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.
2, [[1,0],[0,1]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.
题意及分析:课程之间有依赖关系,问是否能安排课程表。从图论的角度来看就是:有向图是否存在环,存在就false,否则就返回true。最开始我想到的是暴力求解,直接对每个点查找是否能返回到该点,若有则直接返回false,最后超时。判断一个有向图是否有环,可以使用拓扑排序:
一个 DAG 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:
- 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
- 从图中删除该顶点和所有以它为起点的有向边。
- 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
超时代码:
public class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { int row = prerequisites.length; if(row==0) return true; HashMap<Integer,List<Integer>> hashMap = new HashMap<>(); //记录每个出发点及其能到达的点,list为学习该门课程需要学习的先序课程 for(int i=0;i<row;i++){ int[] edge = prerequisites[i]; int begin = edge[0]; //课程 int end = edge[1]; //需要的先序课程 if(hashMap.containsKey(begin)){ hashMap.get(begin).add(end); }else{ List<Integer> temp = new ArrayList<>(); temp.add(end); hashMap.put(begin,temp); } } Queue<Integer> tempQueue = new LinkedList<>(); //用来进行宽度遍历,需要弹出的 Iterator iter = hashMap.entrySet().iterator(); while(iter.hasNext()){ Queue<Integer> queue = new LinkedList<>(); //记录从该点开始被遍历过的点 Map.Entry entry = (Map.Entry) iter.next(); int key = (Integer) entry.getKey(); tempQueue.add(key); //该队列用来对该点就行宽度遍历查找 while(!tempQueue.isEmpty()){ int num = tempQueue.poll(); List<Integer> neighbor = hashMap.get(num); //该点能到达的点 if(!queue.contains(num)){ //该点未被遍历过 queue.add(num); if(neighbor!=null){ //若有该点能到达的点 for(int i=0;i<neighbor.size();i++){ if(key==(neighbor.get(i))) { //包含循环,又回到起始点 return false; }else { // queue.add(neighbor.get(i)); tempQueue.add(neighbor.get(i)); } } } } } } return true; } }
拓扑排序代码:
public class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { int[] map = new int[numCourses]; //记录每个点的入度 for(int i=0;i<prerequisites.length;i++){ //计算入度 map[prerequisites[i][1]]++; } Queue<Integer> queue = new LinkedList<>(); //保存没有前驱的点,即入度为0的点 for(int i=0;i<map.length;i++){ if(map[i]==0) queue.add(i); } int count = queue.size(); //用来记录被删除的点的个数,若和课程数相等则可以排出课程 while (!queue.isEmpty()){ int key = queue.poll(); //删除的课程,并将该课程的所能达到的所有点的入度减1 for(int i=0;i<prerequisites.length;i++){ if(key==prerequisites[i][0]){ int l = prerequisites[i][1]; map[l]--; //入度减1 if(map[l]==0){ //入度为0,添加进queue queue.add(l); count++; } } } } return count==numCourses; } }