老规矩上题目2333:
你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:
很明显的拓扑排序。也就是说,如果这个有向图可以满足拓扑排序的要求,则一定可以满足题目的要求。
而拓扑排序的要求:没有环。
所以说到底就是要检查该有向图中是否有环。
一般检查有无环,使用DFS或者BFS解决即可。(自己在实现时使用BFS)
在使用BFS时,利用一个queue来保存下一次要遍历的点。
而根据拓扑的规则:对于点A,当且仅当点A的入度为0时才可以对其进行遍历。
所以各点入队的判断标准为:入度为零。
所以,必须要在执行BFS之前将各点的入度记录下来:
int []enterd = new int[numCourses];
//记录每个点的入度
for(int i = 0;i < n;i++)
enterd[prerequisites[i][0]]++;
然后再进行bfs:
while(!queue.isEmpty()){
int sp = queue.removeFirst();
// enterd[sp]--;
count++; //遍历的点统计
for(int i = 0;i < n;i++){
if(prerequisites[i][1] != sp) continue; //此边的起点不是当前的点时可以直接continue。
enterd[prerequisites[i][0]]--; //此时的边i一定是以sp为起点的边,所以将终点的入度减一
if(enterd[prerequisites[i][0]] == 0) //如果入度以及减为0,则满足入队条件,可以入队。
queue.addLast(prerequisites[i][0]);
}
}
实现:
java版:
package leetcode;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/*
USER:LQY
DATE:2020/8/4
TIME:8:20
*/
public class leetcode_207 {
public static void main(String []args){
int n = 2;
int [][] pre = {
// {1,0}
// {0,1}
// {1,0},{2,6},{1,7},{5,1},{6,4},{7,0},{0,5}
{1,0},{0,3},{0,2},{3,2},{2,5},{4,5},{5,6},{2,4}
};
System.out.println(new leetcode_207().canFinish1(7, pre));
}
public boolean canFinish1(int numCourses, int[][] prerequisites) {
int n = prerequisites.length;
int []enterd = new int[numCourses];
//记录每个点的入度
for(int i = 0;i < n;i++)
enterd[prerequisites[i][0]]++;
//将入度为0的点加入队列
LinkedList<Integer> queue = new LinkedList<>();
for(int i = 0;i < numCourses;i++){
if(enterd[i] == 0) queue.add(i);
}
//bfs
int count = 0;
while(!queue.isEmpty()){
int sp = queue.removeFirst();
// enterd[sp]--;
count++; //遍历的点统计
for(int i = 0;i < n;i++){
if(prerequisites[i][1] != sp) continue; //此边的起点不是当前的点时可以直接continue。
enterd[prerequisites[i][0]]--; //此时的边i一定是以sp为起点的边,所以将终点的入度减一
if(enterd[prerequisites[i][0]] == 0) //如果入度以及减为0,则满足入队条件,可以入队。
queue.addLast(prerequisites[i][0]);
}
}
//如果统计的遍历过的节点数与想要学习的课程数目一样则返回true
return count==numCourses;
}
// int n;
public boolean canFinish(int numCourses, int[][] prerequisites) {
int n = prerequisites.length;
int []dot = new int[numCourses];
int []book = new int[numCourses];
LinkedList<Integer> queue = new LinkedList<>();
LinkedList<Integer> looked = new LinkedList<>();
//找出入度为0的点
for(int i = 0;i < n;i++){
dot[prerequisites[i][0]]++;
}
//如果没有入度为0的点,false
// int f = 1;
// for(int i = 0;i < numCourses;i++){
// if(dot[i] == 0){
// f = 0;
// System.out.println("点"+i+"的入度为0");
// }
//
// }
// if(f == 1) return false;
for(int i = 0;i < numCourses;i++){
if(dot[i] != 0) continue;
// if(looked.contains(i)) continue;
//bfs
queue.clear();
Arrays.fill(book, 0);
queue.add(i);
// book[i] = i + 1;
while(!queue.isEmpty()){
int sp = queue.removeFirst();
for(int j = 0;j < n;j++){
if(prerequisites[j][1] != sp) continue;
if(book[prerequisites[j][0]]==2 && book[sp]==2) return false;
System.out.println(sp+"->"+prerequisites[j][0]);
queue.addLast(prerequisites[j][0]);
}
//推迟到现在标记
if(!looked.contains(sp)) looked.add(sp);
book[sp]++;
}
}
System.out.println("走过:"+looked.size());
for(int i : looked){
System.out.print(i+" ");
}
if(looked.size() != numCourses) return false;
//最后判断:如果有哪一个book[i] == 2,则说明有环。
return true;
}
}
Python版:
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
n = len(prerequisites)
enterd = [0 for _ in range(numCourses)]
for i in range(n):
enterd[prerequisites[i][0]] += 1
que = []
for i in range(numCourses):
if enterd[i] == 0:
que.append(i)
while(len(que) > 0):
sp = que[0]
que = que[1:]
numCourses -= 1
for i in range(n):
if prerequisites[i][1] != sp:
continue
np = prerequisites[i][0]
enterd[np] -= 1
if enterd[np] == 0:
que.append(np)
return numCourses == 0
C++版:
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
int n = prerequisites.size();
int enterd[numCourses];
for(int i = 0;i < numCourses;i++)
enterd[i] = 0;
for(int i = 0;i < n;i++){
enterd[prerequisites[i][0]]++;
}
queue<int> que;
for(int i = 0;i < numCourses;i++)
if(enterd[i] == 0) que.push(i);
while(!que.empty()){
int sp = que.front();
que.pop();
numCourses--;
for(int i = 0;i < n;i++){
if(prerequisites[i][1] != sp) continue;
int np = prerequisites[i][0];
enterd[np]--;
if(enterd[np] == 0) que.push(np);
}
}
return numCourses == 0;
}
};