课程表-算法设计

从今天开始 接下来的几个月 里我都在这里分享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显示通过!

你可能感兴趣的:(算法与数据结构)