【LeetCode-中等题】207. 课程表

文章目录

    • 题目
    • 方法一:bfs广度优先 + 有向图的拓扑排序(入度)
    • 方法二:dfs 深度优先搜索

题目

【LeetCode-中等题】207. 课程表_第1张图片
【LeetCode-中等题】207. 课程表_第2张图片

此题就可以转换为,求一个有向图是否存在环;
存在环,拓扑排序得出的结果是不完整的,
如果不存在环,则拓扑排序得出的结果就是完整的节点值(拓扑排序不唯一)

怎么判断有环和无环,就看从任意一个点出发,按照箭头方向走,会不会走到以及走过的地方,如果是就是有环

方法一:bfs广度优先 + 有向图的拓扑排序(入度)

思路:将题目中的 prerequisites = [[1,0],[2,0],[3,1],[4,3],[4,2],[5,4]]数组,
按照规则prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
画出有向图:

【LeetCode-中等题】207. 课程表_第3张图片
解题步骤:

  1. 先将题目的数组按照题目意思,转为有向图

  2. 构造入度数组(里面包括每个课程的入度) 课程编号 = 数组下标 数组值为入度(为什么,因为题目限制了课程号必须在课程总数内)
    【LeetCode-中等题】207. 课程表_第4张图片
    【LeetCode-中等题】207. 课程表_第5张图片

  3. 构造课程号集合,集合中每个元素同样为子集合,存储着课程号指向的课程号(记录出度指向,也就是指向元素的入度)
    【LeetCode-中等题】207. 课程表_第6张图片

  4. 从课程号中取出入度为0的元素,加入队列中,然后根据课程号集合,将指向的课程号子集合中的课程号的入度-1,循环结束(队列为空)

  5. 每取出一个入度为0 的元素,就让课程号总数-1,若循环结束(队列为空),课程号总数==0,说明拓扑排序得到的排序序列 = 课程号总数 说明该有向图不存在环,也就满足题目可以完成全部课程,否则有环,就必然不可能完成

【LeetCode-中等题】207. 课程表_第7张图片

// 方法一bfs优化:
public boolean canFinish(int numCourses, int[][] prerequisites) {
    
    int[] cous = new int[numCourses];// 构造入度数组 初始化全部位0  长度为课程数

    Queue<Integer> queue = new LinkedList<>();//存放课程号入度为0的元素  入队再出队

    List<List<Integer>> cousList = new ArrayList<>();//构造课程号集合,集合中每个元素同样为子集合,存储着课程号指向的课程号(记录出度指向,也就是指向元素的入度)

    for(int i = 0 ;i < numCourses ; i++) // 提前给课程号集合创建空子集合
      cousList.add(new ArrayList<>());
 
    for(int[] pre : prerequisites){// 给入度数组附上 入度值,以及给课程号集合的子集合设置好出度课程和入度课程的映射
       cous[pre[0]]++;
       cousList.get(pre[1]).add(pre[0]);
    }
    for(int i = 0 ; i<numCourses ; i++){//从课程号数组中取出课程号的入度为0的元素,将课程号加入队列中
      if(cous[i]==0)  queue.offer(i);
    }

    while(!queue.isEmpty()){
        int ids =  queue.poll();//取出队列入度为0的元素
        numCourses--;//取出一个入度为0 的元素  就让课程总数-1
        for(int cur : cousList.get(ids)){// 根据入度为0 的课程号到课程号集合找到 指向的课程号集合,
          if(cous[cur] >= 1) cous[cur]--;// 如果入度大于>=1将集合的课程号入度-1
          if(cous[cur] == 0) queue.offer(cur); //如果入度==0  则将课程号加入到队列
        }
    }

    if(numCourses == 0) return true;//如果numCourses--到了0  说明该课程构成的有向图 无环 满足题目所有课程都能学习
    else return false;//否则不满足
    
  }

方法二:dfs 深度优先搜索

思路:

  1. 将课程号数组,以下标为课程,数值初始化为0
  2. 构建课程号指向的课程号集合
  3. 寻找课程号数组值为0的课程号进行dfs深度优先
  4. 深度优先现将当前课程号设置为1,然后根据课程号指向的课程号集合寻找下一个课程号为0 的课程号继续dfs,dfs后判断标志位是否置为fasle了,如果是,就取反结束dfs,若不是则继续判断课程号数组的值是否为1,是就代表有环,直接返回false,最后都不满足,说明无环,将当前dfs的元素课程号的值置为2

【LeetCode-中等题】207. 课程表_第8张图片

  List<List<Integer>> cousList;
    int[] cous;
    boolean valid = true;
public boolean canFinish(int numCourses, int[][] prerequisites) {
    
     cous = new int[numCourses];// 构造入度数组 初始化全部位0  长度为课程数

     cousList = new ArrayList<>();//构造课程号集合,集合中每个元素同样为子集合,存储着课程号指向的课程号(记录出度指向,也就是指向元素的入度)

    for(int i = 0 ;i < numCourses ; i++) // 提前给课程号集合创建空子集合
      cousList.add(new ArrayList<>());
 
    for(int[] pre : prerequisites){// 给课程号集合的子集合设置好出度课程和入度课程的映射
       cousList.get(pre[1]).add(pre[0]);
    }

    for(int i = 0 ; i<numCourses ; i++){//
      if(cous[i] == 0)  dfs(i);  //如果是未搜索的科目,则深搜
    }

    return valid;

  }
     
    public void dfs(int c){
        cous[c] = 1;
        for(int cur : cousList.get(c)){//遍历该科目指向的学科
          if(cous[cur]==0){//如果指向的某一学科未搜索过,则深搜
            dfs(cur);
            if(!valid){
              return;
            }
          }else if(cous[cur]==1){//如果指向的某一学科在搜索中,则有环,标记Vaild
                valid = false;
                return;
          }

        }
    cous[c]=2;//因为该科目已经完成深搜,所以标记它的状态搜索完成
  }

你可能感兴趣的:(力扣,#,中等题,leetcode,算法,职场和发展)