leetcode 207. 课程表

题目描述:

现在你总共有 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。这是不可能的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
 

/**其实这道题是数据结构里面图的例子,在讲拓扑排序那一节,无奈图这块忘得一干二净,而且当时这一章也着实学得一般
这个问题的关键对于我来说就是怎么把课程表间的关系转换成有向图了,还有就是inDegree的话,其实也就是逆邻接表,书本里是用了个topo[]的数组存储了拓扑序列,专门记录顶点序号,再弄个顶点的计数器count,最后比较拓扑里面存的个数count与顶点数是否相等来判断是否有环**/
/*解一: 拓扑排序法*/
/*来自大神的题解:
  作者:jyd
  链接:https://leetcode-cn.com/problems/two-sum/solution/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa/
*/
class Solution {
    private int[] inDegree;
   
    public boolean canFinish(int numCourses, int[][] pre) {
        //记录所有点的入度
       inDegree = new int[numCourses];
            //[0<-1]:即1->0所以入度应是每一行的第0列,而不是第1列
            //初始化入度
        for(int[] cp :pre){
            inDegree[cp[0]]++;
        }
        
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 0; i < numCourses;i++){
            if(inDegree[i] == 0) 
                queue.offer(i);//入度为0的结点
        }
        
        while(!queue.isEmpty()){
            int k = queue.poll();
            numCourses--;//去掉入度为0的点
            
            for(int[] preV : pre){
                if(preV[1] == k){
                    inDegree[preV[0]]--;
                    if(inDegree[preV[0]] == 0)
                        queue.offer(preV[0]);
                }
            }
                       
        }
        return numCourses == 0;
    }
}

/*解二: DFS深度遍历有向图*/
/*
 老实说,一直没去想有向图的深度遍历要怎么搞orz,
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/two-sum/solution/tuo-bu-pai-xu-by-liweiwei1419/
*/
//有向图的dfs
class Solution {
 
    public boolean canFinish(int numCourses, int[][] pre) {
        if(numCourses <= 0)
            return false;
        if(pre.length == 0)
            return true;
     
        //邻接表
     //  Set<Integer>[] graph = new HashSet<Integer>[numCourses];
       Set<Integer>[] graph = new HashSet[numCourses];
        
        for(int i = 0; i < numCourses;i++)
            graph[i] = new HashSet<>();
        //顶点的邻接点们
        for(int[] cp : pre){
            graph[cp[1]].add(cp[0]);
        }
        int[] mark = new int[numCourses];
        for(int i = 0; i < numCourses;i++){
            //存在环
            if(dfs(graph, mark, i)){
               return false;   
            }
        }
        return true;
    }
    private boolean dfs(Set<Integer>[] graph, int[] mark,int index){
        
        //一开始mark[index]==0成功执行下面的程序
        if(mark[index] == 1)
            // 从正在访问中,到正在访问中,表示遇到了环
            return true;
        
        // 表示在访问的过程中没有遇到环,这个节点访问过了
        if(mark[index] == 2)
            return false;
        //正在访问graph[index]的邻接表中,mark[index]=1
        mark[index] = 1;
        //邻接结点们
        Set<Integer> successors = graph[index];
        for(Integer successor : successors){
            //层层递归返回true, 说明存在环
           if( dfs(graph,mark,successor))
               return true;
        }
        //遍历完了,没存在环
        mark[index] = 2;
        return false;
    }
}   

啊,我什么时候才可以行云流水般地解出题目来 QAQ

相关知识点:

图, 图的深度优先遍历,图的广度优先遍历,拓扑排序

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章