207. Course Schedule (Medium)&& 210. Course Schedule II(Medium)

来源:互联网 发布:数据行业 编辑:程序博客网 时间:2024/06/01 08:25

原题目:
   There are a total of n courses you have to take, labeled from 0 to n - 1.

  Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

  Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.

  There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.

For example:

2, [[1,0]]There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1]4, [[1,0],[2,0],[3,1],[3,2]]There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is[0,2,1,3].

题目大意如下:
  给课程进行拓扑排序。
  题目一为判断是否能拓扑排序,即图中是否有圈。
  题目二为给定总的课程数n和一系列先决条件的pairs,如果可以全部按次序完成,则返回其中任意一种结果,如果不能完成(图中有圈),则返回空的vector。
  比如:
  pair[0,1]表示课程编号为1的必须在0号之前完成。

解题思路:
  拓扑排序,用BFS和DFS都是可以的,DFS写起来相对简单一点。
  DFS算法有效的原因:
  当我们要向答案中添加元素n时,我们可以确保所有依靠于n的元素已经被加入到答案中了,因为是DFS所以是后处理的元素先进入答案,因此每一次添加元素都是合法的。
  这是题目二的代码,对于题目一只需要在visit函数中,一旦发现temp_visit已经置为1就证明有圈存在,返回false即可。
  DFS:

class Solution {public:    vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {        vector<unordered_set<int> > g(numCourses) ;  //用set可以减少空间的浪费        for(auto i : prerequisites)     //将pairs变成图的形式            g[i.second].insert(i.first) ;        vector<int> ans ;        //per_visit表示总的访问记录(即答案中是否已经存放了这个点的记录)        //temp_visit表示在沿着一个点DFS下的访问记录(用于检测是否有圈存在)        vector<bool> per_visit(numCourses , false) , temp_visit(numCourses , false) ;         //找到没有被访问的一个点做DFS        for(int i = 0 ; i < g.size() ; ++i)            if(!per_visit[i])                if(visit(i , ans , per_visit , temp_visit , g) == false) return {} ;        //因为DFS是从后往前返回(先进后出),所以需要倒转        reverse(ans.begin() , ans.end()) ;        return ans ;    }    bool visit(int pos , vector<int> & ans , vector<bool>& per_visit ,     vector<bool>& temp_visit ,vector<unordered_set<int> > &g         /*这个图的引用很重要,因为后面两个数据量特别大的case会TLE或者MLE*/){        //如果在当前DFS路径上访问到了已经访问的点,说明这个图为非DAG,不能拓扑排序        if(temp_visit[pos]) return false ;        //如果没有把这个点加入到答案中        if(!per_visit[pos]){            temp_visit[pos] = true ;            for(int i : g[pos])                if(visit(i , ans , per_visit , temp_visit , g) == false) return false ;            per_visit[pos] = true ;            temp_visit[pos] = false ;            ans.push_back(pos) ;        }        return true ;    }};

运行结果:
运行结果

  BFS:(计算度数的地方应该可以优化,我这样做特别慢…)

class Solution {public:    vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {        vector<int> ans ;        ans.clear() ;        vector<vector<int> > in_degree (numCourses , vector<int>()) ;        vector<bool> visit(numCourses , false) ;        for(auto i : prerequisites)            in_degree[i.first].push_back(i.second) ;        int mark = -1 ;         //每次把入度为0的没有访问过的点放到答案中        while(ans.size() != numCourses){            for(int i = 0 ; i < in_degree.size() ; ++i){                if(in_degree[i].empty() && !visit[i]){                    ans.push_back(i) ;                    visit[i] = true ;                    //更新每个序号的入度                    com_degree(numCourses ,in_degree , i) ;                }             }            //如果ans.size()没有变说明有圈            if(mark == ans.size()) return {} ;            if(ans.empty()) return {} ;            mark = ans.size() ;        }        return ans ;    }    //更新入度    void com_degree(int numCourses, vector<vector<int> > &in_degree , int pos){        for(int i = 0 ; i < in_degree.size() ; ++i){            if(i != pos){                vector<int>::iterator it = find(in_degree[i].begin() , in_degree[i].end() , pos) ;                if(it != in_degree[i].end()) in_degree[i].erase(it) ;            }        }    }};

知识补充:
  1.当数据量变得很大时,使用引用传递可以节省大量时间和空间;
  2.多使用algorithm里的reverse(BidirectionalIterator first, BidirectionalIterator last)函数。
  3.关于拓扑排序:
  https://en.wikipedia.org/wiki/Topological_sorting#Algorithms

0 0