207. 课程表(拓扑排序bfs)

 

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。

  • 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

 

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

 

 
 

题意解释

我们用有向图来展现这种依赖关系(做事情的先后关系):

 

 

这种叫 有向无环图,把一个 有向无环图 转成 线性的排序 就叫 拓扑排序。
有向图有 入度 和 出度 的概念:
如果存在一条有向边 A --> B,则这条边给 A 增加了 1 个出度,给 B 增加了 1 个入度。
所以,顶点 0、1、2 的入度为 0。顶点 3、4、5 的入度为 2。
每次只能选你能上的课
次只能选入度为 0 的课,因为它不依赖别的课,是当下你能上的课。
假设选了 0,课 3 的先修课少了一门,入度由 2 变 1。
接着选 1,导致课 3 的入度变 0,课 4 的入度由 2 变 1。
接着选 2,导致课 4 的入度变 0。
现在,课 3 和课 4 的入度为 0。继续选入度为 0 的课……直到选不到入度为 0 的课。
这很像 BFS
让入度为 0 的课入列,它们是能直接选的课。
然后逐个出列,出列代表着课被选,需要减小相关课的入度。
如果相关课的入度新变为 0,安排它入列、再出列……直到没有入度为 0 的课可入列。


怎么判断能否修完所有课?
BFS 结束时,如果仍有课的入度不为 0,无法被选,完成不了所有课。否则,能找到一种顺序把所有课上完。
或者:用一个变量 count 记录入列的顶点个数,最后判断 count 是否等于总课程数。

作者:xiao_ben_zhu
链接:https://leetcode.cn/problems/course-schedule/solution/bao-mu-shi-ti-jie-shou-ba-shou-da-tong-tuo-bu-pai-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 
 
 
 1 class Solution {
 2 public:
 3     bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
 4 
 5         unordered_map<int,std::vector<int>> map;
 6         vector<int> ingreed(numCourses,0);
 7         for(int i = 0; i < prerequisites.size();i++) {
 8             ingreed[prerequisites[i][0]]++;
 9             map[prerequisites[i][1]].emplace_back(prerequisites[i][0]);
10         }
11         queue<int> q;
12         for(int i = 0; i < ingreed.size();i++) {
13             if (ingreed[i]==0) {
14                 q.push(i);
15             }
16         }
17         int cnt = 0;
18         while(!q.empty()) {
19             int top = q.front();
20             q.pop();
21             cnt++;
22             for(auto adj: map[top]) {
23                 ingreed[adj]--;
24                 if (ingreed[adj]==0) {
25                     q.push(adj);
26                 }
27             }
28         }
29         return cnt == numCourses;
30     }
31 };

 

 

 

 

DFS

class Solution {
public:
    bool has_circle = false;   
    void dfs(vector<bool>& visted,vector<bool>& on_path, unordered_map<int,std::vector<int>>& map, int node) {
        if (on_path[node]) {
            has_circle = true;
        }
        if (has_circle || visted[node]) {
            return;
        }
        on_path[node] = true;// 都得在循环外面,不然 node 节点访问不到
        visted[node] = true;// 都得在循环外面,不然 node 节点访问不到
        for(auto nebor: map[node]) {
            dfs(visted,on_path,map,nebor);
        }
        on_path[node] = false;

    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {

        unordered_map<int,std::vector<int>> map;
        // 建图
        for(int i = 0; i < prerequisites.size();i++) {
            map[prerequisites[i][1]].emplace_back(prerequisites[i][0]);
        }

        vector<bool> visted = vector<bool>(numCourses,false);
        vector<bool> on_path = vector<bool>(numCourses,false);
        for(int i = 0; i < numCourses; i++) {//注意图中并不是所有节点都相连,所以要用一个 for 循环将所有节点都作为起点调用一次 DFS 搜索算法。

            dfs(visted,on_path,map,i);
        }
        return !has_circle;
    }
};

 

 

posted @ 2022-05-11 10:13  乐乐章  阅读(68)  评论(0编辑  收藏  举报