zoukankan      html  css  js  c++  java
  • LeetCode Notes_#210 课程表II

    LeetCode Notes_#210 课程表II

    Contents

    题目


    解答

    这一题其实就是#207 课程表的升级版,区别在于:

    • 课程表一题只需要返回一个boolean变量,表示这组课程能否被修完;
    • 本题需要给出这组课程正确的修读顺序。

    方法1:BFS+队列

    模拟拓扑排序的过程,参见什么是拓扑排序(Topological Sorting)

    class Solution {
        public int[] findOrder(int numCourses, int[][] prerequisites) {
            int[] res = new int[numCourses];
            int index = 0;
            //每个节点的入度
            int[] inDegrees = new int[numCourses];
            //每个节点指向的节点(后续课程)
            List<List<Integer>> adjacency = new ArrayList<>();
            //队列用于BFS
            Queue<Integer> queue = new LinkedList<>();
            //根据prerequisites填充inDegrees和adjacency
            //adjacency的元素是ArrayList,需要进行初始化
            for(int i = 0; i < numCourses; i++)
                adjacency.add(new ArrayList<>());
            //填充inDegrees和adjacency
            for(int[] cp: prerequisites){
                inDegrees[cp[0]]++;
                adjacency.get(cp[1]).add(cp[0]);
            }
            //初始化队列,将所有入度为0的节点入队
            for(int i = 0; i < numCourses; i++){
                if(inDegrees[i] == 0) queue.offer(i);
            }
            //BFS过程:1. 队列里的课程可修,直接删除 2. 修完一些课程,它的邻居课程可能也可以修了,继续入队
            while(!queue.isEmpty()){
                int pre = queue.poll();
                res[index++] = pre;
                numCourses--;
                for(Integer cur: adjacency.get(pre)){
                    if(--inDegrees[cur] == 0) queue.offer(cur);
                }
            }
            if(numCourses == 0) return res;
            return new int[0];
        }
    }

    复杂度分析

    时间复杂度:O(n+m),n为课程数(图的节点数),m为先修课程要求数(图的边个数)
    空间复杂度:O(n+m),需要先将图存储为邻接表形式(即adjacency形式),他的大小就是n+m。至于其他的辅助变量queue,inDegrees空间都是n

    方法2:DFS+栈

    class Solution {
        List<List<Integer>> edges;
        int[] visited;
        int[] result;
        boolean valid = true;
        int index;
    
        public int[] findOrder(int numCourses, int[][] prerequisites){
            //edges其实就是在统计每个课程的后续课程,edges.get(i)就是一个i课程后续课程的列表
            edges = new ArrayList<List<Integer>>();
            //edges的元素是ArrayList,需要先进行一个初始化
            for(int i = 0; i < numCourses; i++){
                edges.add(new ArrayList<Integer>());
            }
            //visited维护了一个dfs访问的状态:0->未访问 1->已访问但是未入栈 2->已访问且已经入栈
            visited = new int[numCourses];
            //result用来模拟栈,即:从后向前填充数组
            result = new int[numCourses];
            //index初始化为最后一个下标,这样才可以从后向前填充数组
            index = numCourses - 1;
            //填充edges数组,即统计每个课程的后续课程,用于后续的dfs搜索
            for(int[] info: prerequisites){
                edges.get(info[1]).add(info[0]);
            }
            //对每一个课程进行dfs搜索,如果在某个课程的dfs课程中,已经遇到了环,可以直接跳出for循环过程
            for(int i = 0; i < numCourses && valid; i++){
                if(visited[i] == 0) dfs(i);
            }
            //根据valid变量决定返回值,如果valid == false说明这些课程不可能修完,直接返回空数组
            if(!valid) return new int[0];
            //否则就返回result数组,记录了正确的完成课程的顺序
            return result;
        }
        //为什么这里的递归没有出口条件?其实出口条件有两个:1. edges.get(u)全都遍历了一遍,就可以返回了 2. 中途遇到了valid == false的情况,就不会把edges.get(i)遍历一遍,而是提前返回
        public void dfs(int u){
            //访问到一个课程,首先修改visit标记,表示当前课程正在被访问,但是还没有入栈(因为这还不是最后一个课程,需要进一步进行dfs)
            visited[u] = 1;
            //对当前课程的所有后续课程进行dfs(类似对树的左右子树进行dfs)
            for(int v: edges.get(u)){
                if(visited[v] == 0){
                    //dfs的主要目的就是判断是否有环(或者说维护valid变量)
                    dfs(v);
                    if(!valid) return;
                }else if(visited[v] == 1){
                    //遇到了之前标记过的课程,就说明出现了环,修改valid变量为false,直接结束一切过程,返回空数组
                    valid = false;
                    return;
                }
            }
            //课程入栈
            visited[u] = 2;
            result[index--] = u;
        }
    }

    复杂度分析

    时间复杂度:O(n+m),n为课程数(图的节点数),m为先修课程要求数(图的边个数)
    空间复杂度:O(n+m),需要先将图存储为邻接表形式(即edges形式),他的大小就是n+m。至于其他的辅助变量result,visited空间都是n。递归调用的栈深度最高不会超过n(高度为n的情况下,图退化成为一个链表)。

  • 相关阅读:
    Spring--IOC
    神奇的小东西
    视图层发起请求的方式
    jdbc “贾琏欲执事”
    线程的五种状态
    java的<<左移,>>右移,>>>无符号右移
    直接插入排序(单链表排序)
    单链表相关知识以及指针引用相关知识
    将一个数n分解为若干个从小到大排列的质数的积 ,求质数因子
    最小二乘法
  • 原文地址:https://www.cnblogs.com/Howfars/p/14327098.html
Copyright © 2011-2022 走看看