从今天开始 接下来的几个月 里我都在这里分享leecode解题思路和蓝桥杯历届试题的解题思路 。。。
第一天先看一个dfs的题目 :
现在你总共有 n 门课需要选,记为 0 到 n-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。这是不可能的。
我们一看到这样的题目,基本上都是dfs的类型,所有dfs的题目都可以用图来表示的,这个题目用图表示再好不过了,
1. 有一点我们必须要知道,怎么样才能算完成不了课程?
我们可以清晰的发现,当一个课程i 的先行课是j的话,而j的先行课与i又有关系,那我们就完成不了i和j,
举个例子:课程0的先行课是1,1的先行课是0,这样就矛盾了,所以我们完成不了课程,
所以我们的思路就是根据题目所给信息,构造图,图的表示方式为邻接表,对邻接表进行遍历,发现如果存在环状图,则不行
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 先构造图 用邻接表的方式构造图
HashSet<Integer>[] gra = new HashSet[numCourses];
// 此邻接表的含义是 gra[i] = ...学完 i 课程之后能学习哪些课程
for (int i = 0; i < gra.length; i++) {
gra[i] = new HashSet<>();
}
for (int[] temp : prerequisites) {
gra[temp[1]].add(temp[0]);
}
// 因为他可能是好几个连通图
// 我们设置递归函数 dfs(int i, gra, int[] mark)
// 递归函数的作用是 已从i开始遍历的极大连通子图 是否存在环 存在环 则返回true 不存在返回false
int[] mark = new int[numCourses];
for (int i = 0; i < numCourses; i++) {
// 如果这个节点没有被访问 则递归访问
if (mark[i] == 0) {
if (dfs(i, gra, mark)) {
return false;
}
}
}
// 如果每一个连通子图都没有存在环 就说明这个图 不存在环
return true;
}
// 下面着重写dfs()函数
// i 表示当前正在访问哪个节点 mark为标记
// mark[i] == 0 说明这个点没有被访问
// mark[i] == 1 说明这个点正在被访问
// mark[i] == 2 说明这个点已经被访问完了
private boolean dfs(int i, HashSet<Integer>[] gra, int[] mark) {
// 从在访问这个节点 后来又访问了这个节点 说明存在环 返回true
if (mark[i] == 1) {
return true;
}
// 说明这个点以前已经访问过了 从这点出发是没有环存在的 返回false
if (mark[i] == 2) {
return false;
}
// 当以上两种 都不符合 说明 mark[i] == 0 说明这个节点还没走过 那么我们访问这个节点
// 看看从这个节点出发有没有环
mark[i] = 1; // 标记正在访问 这个极大连通图
for (int j : gra[i]) {
// 这表示如果从 当前节点的邻接节点 是有环的
if (dfs(j, gra, mark)) {
return true;
}
}
// 如果从 i的所有邻接节点都没有环 那就说明当前图是没有环的
mark[i] = 2;
return false;
}
最后leecode显示通过!