LeeCode( dfs,bfs)课程表

LeeCode( dfs,bfs)课程表

题目:
你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-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。这是不可能的。

提示:

  1. 输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。
  2. 详情请参见图的表示法。
  3. 你可以假定输入的先决条件中没有重复的边。 1 <= numCourses <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

  1. 我们可以把每个课程看成一个节点,学习课程B前必须学习课程A,这样就可以把课程表抽象成一个有向图。当我们可以遍历完这个有向图,说明可以完成课程学习。
  2. 把每个节点抽象成三种状态
    1.未搜索
    2.搜索中(已开始遍历,但是还没回溯)
    3.已搜索
  3. 每次dfs都取一个未搜索的节点开始遍历,如果没有遍历到图中的环,说明可以完成遍历。

对于图中的任意一个节点,它在搜索的过程中有三种状态,即:

「未搜索」:我们还没有搜索到这个节点;

「搜索中」:我们搜索过这个节点,但还没有回溯到该节点,即该节点还没有入栈,还有相邻的节点没有搜索完成);

「已完成」:我们搜索过并且回溯过这个节点,即该节点已经入栈,并且所有该节点的相邻节点都出现在栈的更底部的位置,满足拓扑排序的要求。

通过上述的三种状态,我们就可以给出使用深度优先搜索得到拓扑排序的算法流程,在每一轮的搜索搜索开始时,我们任取一个「未搜索」的节点开始进行深度优先搜索。

我们将当前搜索的节点 u 标记为「搜索中」,遍历该节点的每一个相邻节点 v:

如果 v 为「未搜索」,那么我们开始搜索 v,待搜索完成回溯到 u;

如果 v 为「搜索中」,那么我们就找到了图中的一个环,因此是不存在拓扑排序的;

如果 v 为「已完成」,那么说明 v 已经在栈中了,而 u 还不在栈中,因此 u 无论何时入栈都不会影响到 (u, v) 之前的拓扑关系,以及不用进行任何操作。

当 u 的所有相邻节点都为「已完成」时,我们将 u 放入栈中,并将其标记为「已完成」。

在整个深度优先搜索的过程结束后,如果我们没有找到图中的环,那么栈中存储这所有的 n 个节点,从栈顶到栈底的顺序即为一种拓扑排序。

方法一:
dfs

Java代码:

import java.util.ArrayList;
import java.util.List;

public class 课程表 {
	List<List<Integer>> edgs;
	int[] visited;
	boolean valid = true;
	
	public boolean canFinish(int numCourses, int[][] prerequisites) {
		edgs = new ArrayList<List<Integer>>();
		for(int i=0;i<numCourses;i++){
			edgs.add(new ArrayList<>());
		}
		
		visited = new int[numCourses];
		
		for(int info[] : prerequisites){
			edgs.get(info[1]).add(info[0]);
		}
		
		for(int i = 0;i < numCourses && valid;i++){
			if(visited[i] == 0){
				dfs(i);
			}
		}
		return valid;
	}

	private void dfs(int u) {
		visited[u] = 1;
		for(int v : edgs.get(u)){
			if(visited[v]==0){
				dfs(v);
				if(!valid){
					return;
				}
			}else if(visited[v]==1){
				valid = false;
				return;
			}
		}
		visited[u] = 2;
	}
}

方法二:
bfs

import java.util.ArrayList;
import java.util.List;

public class 课程表 {
	 List<List<Integer>> edges;
	    int[] indeg;

	    public boolean canFinish(int numCourses, int[][] prerequisites) {
	        edges = new ArrayList<List<Integer>>();
	        for (int i = 0; i < numCourses; ++i) {
	            edges.add(new ArrayList<Integer>());
	        }
	        indeg = new int[numCourses];
	        for (int[] info : prerequisites) {
	            edges.get(info[1]).add(info[0]);
	            ++indeg[info[0]];
	        }

	        Queue<Integer> queue = new LinkedList<Integer>();
	        for (int i = 0; i < numCourses; ++i) {
	            if (indeg[i] == 0) {
	                queue.offer(i);
	            }
	        }

	        int visited = 0;
	        while (!queue.isEmpty()) {
	            ++visited;
	            int u = queue.poll();
	            for (int v: edges.get(u)) {
	                --indeg[v];
	                if (indeg[v] == 0) {
	                    queue.offer(v);
	                }
	            }
	        }

	        return visited == numCourses;
	    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值