题目描述
你这个学期必须选修 numCourse
门课程,记为 0
到 numCourse-1
。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0
,你需要先完成课程 1
,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
样例
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。
这是不可能的。
提示:
- 1、输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
- 2、你可以假定输入的先决条件中没有重复的边。
- 3、1 <= numCourses <= 10^5
算法分析
拓扑排序
拓扑排序步骤
- 1、由于课程有先后顺序,因此是一个拓扑排序问题,先建边,构图
- 2、走一遍拓扑排序,判断拓扑排序后点的个数
cnt
与总的课程numCourses
是否相等
时间复杂度 $O(n + m)$
Java 代码
class Solution {
static int N = 100010;
static int[] h = new int[N];
static int[] e = new int[N];
static int[] ne = new int[N];
static int[] d = new int[N];
static int idx = 0;
static int n ;
static int m ;
static void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
static boolean topsort()
{
Queue<Integer> q = new LinkedList<Integer>();
for(int i = 0;i < n;i ++)
{
if(d[i] == 0)
q.add(i);
}
int cnt = 0;
while(!q.isEmpty())
{
int t = q.poll();
cnt ++;
for(int i = h[t]; i != -1;i = ne[i])
{
int j = e[i];
//删除当前边
d[j] --;
if(d[j] == 0) q.add(j);
}
}
return cnt == n;
}
public boolean canFinish(int numCourses, int[][] prerequisites) {
n = numCourses;
m = prerequisites.length;
if(m == 0) return true;
Arrays.fill(h, -1);
Arrays.fill(e, 0);
Arrays.fill(ne, 0);
Arrays.fill(d, 0);
for(int i = 0;i < m;i ++)
{
int a = prerequisites[i][0];
int b = prerequisites[i][1];
add(b, a);
d[a] ++;
}
return topsort();
}
}