题目
你这个学期必须选修 numCourses
门课程,记为 0
到 numCourses - 1
。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites
给出,其中 prerequisites[i] = [ai, bi]
,表示如果要学习课程 ai
则 必须 先学习课程 bi
。
- 例如,先修课程对
[0, 1]
表示:想要学习课程0
,你需要先完成课程1
。
请你判断是否可能完成所有课程的学习?如果可以,返回 true
;否则,返回 false
。
示例 1:
输入: numCourses = 2, prerequisites = [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
代码与解析
class Solution {
/**
* 判断课程是否能够完成
*
* @param numCourses 课程数量
* @param prerequisites 课程先修关系的数组
* @return 如果能完成所有课程返回true,否则返回false
*/
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 创建图的邻接表
List<List<Integer>> list = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
list.add(new ArrayList<>());
}
// 初始化状态数组,0表示未访问,1表示正在访问,-1表示已访问
int[] state = new int[numCourses];
// 构建邻接表
for (int[] cp : prerequisites) {
list.get(cp[1]).add(cp[0]); // 记录先修课对应的后续课程
}
// 对每门课程进行深度优先搜索,判断是否有环
for (int i = 0; i < numCourses; i++) {
if (!dfs(list, state, i)) return false; // 若存在环,则返回false
}
return true; // 不存在环,返回true
}
/**
* 深度优先搜索判断是否存在环
*
* @param list 课程的邻接表
* @param state 课程状态数组
* @param need 当前需要判断的课程
* @return 存在环返回false,否则返回true
*/
public boolean dfs(List<List<Integer>> list, int[] state, int need) {
if (state[need] == 1) return false; // 若当前课程正在访问中,说明存在环,返回false
if (state[need] == -1) return true; // 若当前课程已访问过,直接返回true
state[need] = 1; // 标记当前课程正在访问中
// 对当前课程的后续课程进行深度优先搜索
for (Integer t : list.get(need)) {
if (!dfs(list, state, t)) return false; // 若后续课程存在环,返回false
}
state[need] = -1; // 标记当前课程访问结束
return true; // 当前课程不存在环,返回true
}
}
第二次写的代码,虽然也是用的dfs但是超时了,逻辑是对的,超时就意味着写的不行。
class Solution {
boolean[] all;
int[] h, e, ne;
int idx, count;
public void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
public boolean canFinish(int numCourses, int[][] prerequisites) {
all = new boolean[numCourses];
h = new int[numCourses];
e = new int[numCourses * 2 + 10];
ne = new int[numCourses * 2 + 10];
Arrays.fill(h, -1);
for(int[] p : prerequisites) {
add(p[0], p[1]);
}
for(int i = 0;i < numCourses;i ++) {
if(!all[i]) {
boolean[] f = new boolean[numCourses];
f[i] = true;
dfs(i, f);
}
}
for(int i = 0;i < numCourses;i ++) {
if(!all[i]) return false;
}
return true;
}
public boolean dfs(int now, boolean[] f) {
for(int i = h[now];i != -1;i = ne[i]) {
int next = e[i];
if(f[next]) return all[now] = false;
f[next] = true;
dfs(next, f);
f[next] = false;
}
return all[now] = true;
}
}
然后重看了以前我自己写的,用int数组代表已访问未访问呢,这样子会更快一点同时时间复杂度也比以前提高了2ms
class Solution {
boolean[] all;
int[] h, e, ne;
int idx, count;
public void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
public boolean canFinish(int numCourses, int[][] prerequisites) {
all = new boolean[numCourses];
h = new int[numCourses];
e = new int[numCourses * 4 + 10];
ne = new int[numCourses * 4 + 10];
Arrays.fill(h, -1);
for(int[] p : prerequisites) {
add(p[0], p[1]);
}
int[] state = new int[numCourses + 1];
for(int i = 0;i < numCourses;i ++){
if(!dfs(i, state)) return false;
}
return true;
}
public boolean dfs(int now, int[] f) {
if(f[now] == 1) return false; // 正在访问
if(f[now] == -1) return true;
f[now] = 1;
for(int i = h[now];i != -1;i = ne[i]) {
int next = e[i];
if(!dfs(next, f))return false;
}
f[now] = -1;
return true;
}
}