Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目:假如你是一个学生,给定你在这个学期所必修得num门课程,记为0到num-1。选修课程必须按照规则来,即想学一门课程必须完成这个课程得先修课程。请判断是否可能完成所有课程的学习。
解题思路
先修课程的判断实际上是图的拓扑排序,也即判断一个有向无环图的拓扑排序是否存在。
拓扑排序的生成步骤为首先找到入度为0的节点,之后将这个节点的出度节点的入度更新,之后找到下一个入度为0的节点,不断重复直到没有入度为0的节点。
那么对于本题,我们可以首先记录每个课程的入度节点,这些入度节点的个数即为需要先修的课程数量。之后还需要将将本课程作为先修课程的课程给记录下来,此处使用的都是map。如果有入度为0的节点,也就是该课程不需要任何的先修课程,此时可以将本课程入队,之后根据队列是否为空循环出队,出队时更新以此课程作为先修课程的课程入度,如果以此课程为入度课程的课程的入度为0,则将此课程入队,最后看入度map中的课程是否存在入度不为0的课程即可,代码如下:
public boolean canFinish(int numCourses, int[][] prerequisites) {
HashMap<Integer, Integer> inDgree = new HashMap<>();
HashMap<Integer, List<Integer>> map = new HashMap<>();
for(int i=0;i<numCourses;++i){
inDgree.put(i, 0);
}
for(int[] pre:prerequisites){
if(!map.containsKey(pre[1])){
map.put(pre[1], new ArrayList<>());
}
map.get(pre[1]).add(pre[0]);
inDgree.put(pre[0], inDgree.get(pre[0])+1);
}
Deque<Integer> deque = new LinkedList<>();
for(int i=0;i<inDgree.size();i++){
if(inDgree.get(i)==0){
deque.addLast(i);
}
}
while(!deque.isEmpty()){
Integer pollFirst = deque.pollFirst();
if(!map.containsKey(pollFirst)) continue;
for(int value:map.get(pollFirst)){
inDgree.put(value, inDgree.get(value) - 1);
if(inDgree.get(value)==0){
deque.addLast(value);
}
}
}
for(Integer value:inDgree.keySet()){
if(inDgree.get(value)!=0) return false;
}
return true;
}
最终耗时16ms,实际上可以对上述代码进行优化,使用一个变量来记录当前处队的节点个数,最后判断当前出队节点个数是否等于所需要修的课程数即可。这样时间少了一个。如下出队之后的代码改为:
int visited = 0;
while(!deque.isEmpty()){
visited ++ ;
Integer pollFirst = deque.pollFirst();
if(!map.containsKey(pollFirst)) continue;
for(int value:map.get(pollFirst)){
inDgree.put(value, inDgree.get(value) - 1);
if(inDgree.get(value)==0){
deque.addLast(value);
}
}
}
return visited == numCourses;
}
最终耗时10ms,上述代码还可以进一步优化,我们存储入度节点使用的是map,在代码执行过程中,我们需要对入度节点频繁的查询更新,因此入度节点的数据结构我们可以采用数组来实现,必然可以加速代码的执行速度,代码如下:
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] inDgree = new int[numCourses];
HashMap<Integer, List<Integer>> map = new HashMap<>();
for(int[] pre:prerequisites){
if(!map.containsKey(pre[1])){
map.put(pre[1], new ArrayList<>());
}
map.get(pre[1]).add(pre[0]);
inDgree[pre[0]]++;
}
Deque<Integer> deque = new LinkedList<>();
for(int i=0;i<inDgree.length;i++){
if(inDgree[i]==0){
deque.addLast(i);
}
}
int visited = 0;
while(!deque.isEmpty()){
visited ++ ;
Integer pollFirst = deque.pollFirst();
if(!map.containsKey(pollFirst)) continue;
for(int value:map.get(pollFirst)){
inDgree[value]--;
if(inDgree[value]==0){
deque.addLast(value);
}
}
}
return visited == numCourses;
}
这样代码最终耗时为6ms。