题目
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?
Example 1:
Input: 2, [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: 2, [[1,0],[0,1]]
Output: false
Explanation: 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.
Note:
- The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
- You may assume that there are no duplicate edges in the input prerequisites
十分钟尝试
知道这是个有向图,题目其实要求的就是判断这个有向图是否有环。如果有环,就是循环以来,题目无解。下面我们来学习一下如何判断一个有向图是否有环。有三种方法
1. DFS
2. BFS
3 Kahn 算法
有向图BFS算法
BFS的结果是: 5 2 3 4 0 1 或者 5 2 3 4 1 0最后两位取决于二维数组入队列的顺序
看了思路之后,自己代码实现一下。
1. 计算所有节点的入度,存入indegree数组,节点值就是数组索引
2. 遍历indegree数组,入度为0的入队列
3 遍历队列,直到为空,取出队列元素,遍历二维数组,如果“出发”元素为当前节点值,那么所有“子节点”入度减去1(此操作相当于把当前处理的入度为0的节点拿掉)
4 如果子节点入度变为0,入队列
5 最后如果所有节点都能入度为0,则没有环,否则有环
代码有个错误点,判断入度为0入队列的时候,add进去的是j,索引,不是indegree[j]。
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
//BFS
if(numCourses<0){
return false;
}
//计算所有节点的入度,第二列数据
int[] indegree=new int[numCourses];
for(int i=0;i<prerequisites.length;i++){
indegree[prerequisites[i][1]]++;
}
//入度为0的入队列
Deque<Integer> queue=new LinkedList();
for(int j=0;j<indegree.length;j++){
if(indegree[j]==0){
//此处入j不是indegree[j],因为索引就是值
queue.offer(j);
}
}
//循环取出队列元素,开始遍历
while(!queue.isEmpty()){
int curr=queue.poll();
for(int i=0;i<prerequisites.length;i++){
if(curr==prerequisites[i][0]){
indegree[prerequisites[i][1]]--;
if(indegree[prerequisites[i][1]]==0){
queue.add(prerequisites[i][1]);
}
}
}
}
//判断是否存在入度不为0的
for(int i=0;i<indegree.length;i++){
if(indegree[i]!=0){
return false;
}
}
return true;
}
}
有向图的DFS算法
深度优先,很显然我们需要找到最底层的节点,也就是没有出度的节点。
1. 找到出度为0度节点,存入集合S
2 遍历集合S,如果当前节点m未访问,标记为访问
3 所有指向m的节点,递归访问。
4 当前节点m加入结果集
DFS的结果是: 1 3 2 5 0 4
DFS的代码暂时没有彻底理解,先记录一下:
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
//DFS
ArrayList[] graph=new ArrayList[numCourses];
boolean[] visited=new boolean[numCourses];
for(int i=0;i<graph.length;i++){
graph[i]=new ArrayList<Integer>();
}
for(int i=0;i<prerequisites.length;i++){
// graph[i]=prerequisites[i][1];注意这行
graph[prerequisites[i][1]].add(prerequisites[i][0]);
}
for(int i=0;i<numCourses;i++){
if(!dfs(graph,visited,i)){
return false;
}
}
return true;
}
private boolean dfs(ArrayList[] graph,boolean[] visited,int course){
if(visited[course]){
return false;
}
else{
visited[course]=true;
}
for(int i=0;i<graph[course].size();i++){
if(!dfs(graph,visited,(Integer)graph[course].get(i))){
return false;
}
}
visited[course]=false;
return true;
}
}