你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false
https://leetcode-cn.com/problems/course-schedule/
示例1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
提示:
1 <= numCourses <= 105
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同
Java解法
思路:
初步思考,这种有关联的数据类似于链表结构,如果链表有环,那么这个数据将互相死锁无法学习
双向链表:前节点为前置学习课程,后节点为后续学习课程,无前节点可作为学习课程起点学习数量:可以通过起点遍历记数返回
因为没用提供数据结构供解题,这里先采用hashmap来记录对应关系public static boolean canFinish(int numCourses, int[][] prerequisites) { if (prerequisites == null||prerequisites.length==0) { return true; } HashMap<Integer, List<Integer>> map = new HashMap<>();//记录学 a课需要先学的b课 HashMap<Integer, List<Integer>> map2 = new HashMap<>();//记录 已学 a课,可以学的b课 for (int[] prerequisite : prerequisites) { List<Integer> list = map.getOrDefault(prerequisite[0], new ArrayList<>()); list.add(prerequisite[1]); List<Integer> list1 = map.getOrDefault(prerequisite[1], new ArrayList<>()); map.put(prerequisite[0], list); map.put(prerequisite[1], list1); List<Integer> integers = map2.getOrDefault(prerequisite[1], new ArrayList<>()); integers.add(prerequisite[0]); map2.put(prerequisite[1],integers); } List<Integer> start = new ArrayList<>(); for (Map.Entry<Integer, List<Integer>> entry : map.entrySet()) { if (entry.getValue()==null||entry.getValue().size()==0) { start.add(entry.getKey()); } } int count = numCourses; for (Integer integer : start) { count--; int unLearn = learnFinish(map2, integer, count); if (unLearn<=0) { return true; } count = unLearn; } return count<=0; } public static int learnFinish(HashMap<Integer, List<Integer>> map,int index,int count){ List<Integer> integers = map.getOrDefault(index,new ArrayList<>()); count-=integers.size(); for (Integer i : integers) { count -= learnFinish(map, i, count); } return count; }
不知道是理解错误,这里的课程数无关结果,这里要求的是课程是不是能学完,脑壳大
参考官方解:拓扑排序+深度优先搜索+回溯
- 使用数组记录搜索状态,使用栈记录回溯完成遍历节点
package sj.shimmer.algorithm.m4_2021;
import java.util.ArrayList;
import java.util.List;
/**
* Created by SJ on 2021/4/19.
*/
class D82 {
public static void main(String[] args) {
System.out.println(canFinish(2,new int[][]{
{1,0},
{2,1},
}));
System.out.println(canFinish(20,new int[][]{
{0,10},
{3,18},
{5,5},
{6,11},
{11,14},
{13,1},
{15,1},
{17,4},
}));
System.out.println(canFinish(1,new int[][]{}));
}
static List<List<Integer>> edges;
static int[] visited;//0 未搜索,1 搜索中(有环,无法完成),2 完成
static boolean valid = true;
public static boolean canFinish(int numCourses, int[][] prerequisites) {
edges = new ArrayList<List<Integer>>();
for (int i = 0; i < numCourses; ++i) {
edges.add(new ArrayList<Integer>());
}
visited = new int[numCourses];
for (int[] info : prerequisites) {
edges.get(info[1]).add(info[0]);//记录学完 可以学的课,
}
for (int i = 0; i < numCourses && valid; ++i) {
if (visited[i] == 0) {
dfs(i);//开始深度搜索
}
}
return valid;
}
public static void dfs(int u) {
visited[u] = 1;
for (int v: edges.get(u)) {
if (visited[v] == 0) {
dfs(v);
if (!valid) {
return;
}
} else if (visited[v] == 1) {
valid = false;
return;
}
}
visited[u] = 2;
}
}
官方解
https://leetcode-cn.com/problems/course-schedule/submissions/
-
深度优先搜索
如上
- 时间复杂度: O(n+m)
- 空间复杂度: O(n+m)
- 广度优先搜索:上方逆序操作