题目的链接在这里:https://leetcode-cn.com/problems/course-schedule-ii/
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。
可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。
深度遍历和广度遍历
代码如下:
class Solution { //存储有向图 List<List<Integer>> edges; //标记每个节点的状态 0=未搜索 1=搜索中 2=已完成 int[] visited; //用数组来模拟栈 下标n-1为栈底 0为栈顶 int[] result; //判断有向图是否有环 boolean valid=true; //找下标 int index; public int[] findOrder(int numCourses, int[][] prerequisites) { //深度优先搜索 用栈来存储所有已经搜索完的节点 /** * 对于一个节点,如果从这个节点出发通过一条有向边可以达到的所有节点都已经搜索完成 了那这个点 被回溯到的时候 就会变成一个已经搜索完成的节点 * 因为栈是先进后厨 所以那些先进栈的会后出栈 * 思路就是:对图进行一次深度优先遍历 每个节点进行回溯的时候 把节点放入栈中,从栈顶到栈底的序列就是一种拓扑排序 * 节点有三个状态 * 1.未搜索 没有搜索到这个节点 * 2.搜索中 搜索过这个节点 但没有回溯到这个节点 也就是这个节点还没入栈 * 3.已完成 搜索并回溯过这个节点 他已经入栈 并且所有该节点的相邻节点都出现在栈的更底部 * * 先对任意一个未搜索的节点u进行深搜 把他标记为搜索中 然后开始搜索他的相邻节点 并且 如果他是未搜索的话 就开始搜索这个 * 节点 等搜索完成之后回溯到u 如果v是搜索中的话 就相当于是找到图的一个环 那就不存在拓扑排序 * 如果是已完成的 那就说明v已经在栈中 而u不在栈中 * 当u所有相邻的u都表示为已完成时,把u放入到栈中,并标记为 已完成 */ edges=new ArrayList<List<Integer>>(); for(int i=0;i<numCourses;i++){ edges.add(new ArrayList<Integer>()); } //初始化全是0 visited=new int[numCourses]; result=new int[numCourses]; index=numCourses-1; for(int info[]:prerequisites){ //遍历二维数组中的每一个数组 只能说明前面那个add已经好了 edges.get(info[1]).add(info[0]); } //每次挑选一个 未搜索的节点 开始深度优先搜索 for(int i=0;i<numCourses&&valid;i++){ if(visited[i]==0){ //说明没有被访问过 dfs(i); } } if(!valid){ //说明出现了环 return new int[0]; } return result; } private void dfs(int i) { //把这个点改成搜索中 visited[i]=1; //搜素相邻的节点 他相邻的点 就是从这个点出去的 //只要发现有环 就停止搜索 for(int v:edges.get(i)){ //也就是从这个点出去的 if(visited[v]==0){ //说明是没有访问过的 那就访问他 dfs(v); if(!valid){ return; } } //然后如果发现在搜索中 就说明找到了环 else if(visited[v]==1){ valid=false; return; } } //然后把这个所谓访问过了 入栈 visited[i]=2; result[index--]=i; } }
代码如下:
class Solution { //存储有向图 List<List<Integer>> edges; //存储每个节点的入度 int[] indeg; //存储答案 int[]res; //答案下标 int index; public int[] findOrder(int numCourses, int[][] prerequisites) { //广度搜索遍历 /** *最前面的节点 一定不会有入边 所有当一个节点加入答案之后,就可以移除他的出边了,也就表示他相邻节点少了一门先修课程的要求 而当某个节点变成了没有入度 * 的节点是 那就是可以放入到大按照你了 * 使用队列来进行广度优先搜索 刚开始将所有入度为0的节点放到队列当中 然后每次都取出队首的节点 * 1.放入答案当中 * 移除所有u的出边 也就是u所有相邻的节点入度减去 并如果存在一个入度为0的话 就让他进入到队列当中 */ edges=new ArrayList<List<Integer>>(); for(int i=0;i<numCourses;i++){ edges.add(new ArrayList<Integer>()); } indeg=new int[numCourses]; res=new int[numCourses]; index=0; for(int[]info:prerequisites){ edges.get(info[1]).add(info[0]); //然后这个点的入度加一 indeg[info[0]]++; } //然后创建一个队列 Queue<Integer> queue=new LinkedList<Integer>(); //将所有入度为0的点加入到队列中 for(int i=0;i<numCourses;i++){ if(indeg[i]==0){ //说明入度是0 queue.add(i); /* queue.offer(i);*/ } } //然后开始判断 while (!queue.isEmpty()){ //从队列头取出第一个 int node=queue.poll(); //然后把他放入到答案当中 res[index++]=node; //然后判断有没有以他出去的点 for(int v:edges.get(node)){ //如果有的话 对应的入度减一 并且如果在其中等于0 的话 就可以进栈了 indeg[v]--; if(indeg[v]==0){ queue.add(v); } } } //最后的判断是 return index==numCourses?res:new int[0]; } }