LeetCode - 207. 课程表

描述

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-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。这是不可能的。
 

提示:

输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
1 <= numCourses <= 10^5

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

 

求解

   class Solution {
    private:
        /*!
         *
         * @param graph 图,邻接表
         * @param inDegree 顶点入度
         * @param items 待排序顶点
         * @return  拓扑排序结果
         */
        vector<int>
        topSort_useStack(const vector<vector<int>> &graph, vector<int> &inDegree, const vector<int> &items) {
            // 找出入度为0的点开始遍历
            std::stack<int> s;
            for (auto i : items) {
                if (inDegree[i] == 0) {
                    s.push(i); // 顶点入度为0,入栈
                }
            }

            // 拓扑排序
            vector<int> res;
            while (!s.empty()) {
                int v = s.top();
                res.push_back(v); // 保存拓扑排序结果
                s.pop();
                for (auto w  : graph[v]) {
                    if (--inDegree[w] == 0) {
                        s.push(w);
                    }
                }
            }

            return res;
        }

        // 利用拓扑排序判断是否存在环,不关心拓扑排序结果
        bool topSortJudgeCircle(const vector<vector<int>> &graph, vector<int> &inDegree, const vector<int> &items) {
            // 找出入度为0的点开始遍历
            std::stack<int> s;
            for (auto i : items) {
                if (inDegree[i] == 0) {
                    s.push(i); // 顶点入度为0,入栈
                }
            }

            // 拓扑排序
            int count = 0;
            while (!s.empty()) {
                ++count; // 拓扑排序节点数累加
                int v = s.top();
                s.pop();
                for (auto w  : graph[v]) {
                    if (--inDegree[w] == 0) {
                        s.push(w);
                    }
                }
            }

            return count == items.size();
        }

        /*!
         *
         * @param graph 图,邻接表
         * @param inDegree 顶点入度
         * @param items 待排序顶点
         * @return 拓扑排序结果
         */
        vector<int>
        topSort_useQueue(const vector<vector<int>> &graph, vector<int> &inDegree, const vector<int> &items) {
            // 找出入度为0的点开始遍历
            std::queue<int> s;
            for (auto i : items) {
                if (inDegree[i] == 0) {
                    s.push(i); // 顶点入度为0,入栈
                }
            }

            // 拓扑排序
            vector<int> res;
            while (!s.empty()) {
                int v = s.front();
                res.push_back(v); // 保存拓扑排序结果
                s.pop();
                for (auto w  : graph[v]) {
                    if (--inDegree[w] == 0) {
                        s.push(w);
                    }
                }
            }

            return res;
        }

    public:
        // 考察判断有向图中是否有环

        // 方法一,拓扑排序判断是否有环,得到拓扑排序结果后校验节点数是否和原节点数一致
        bool canFinish_1e(int numCourses, vector<vector<int>> &prerequisites) {
            vector<int> inDegree(numCourses, 0); // 顶点入度
            vector<vector<int>> graph(numCourses, vector<int>()); // 图,邻接表存储
            // 根据先决条件,计算每个点的入度,同时构造图
            for (auto &p  : prerequisites) {
                ++inDegree[p[0]];
                graph[p[1]].push_back(p[0]);
            }

            vector<int> items(numCourses, 0);
            for (int i = 0; i < numCourses; ++i) {
                items[i] = i;
            }
//            auto res = topSort_useStack(graph, inDegree, items);
            auto res = topSort_useQueue(graph, inDegree, items);
            return res.size() == numCourses; // 拓扑排序后的点数量等于图原始点数量,则没有环,否则<原始点数量证明有环
        }

        // 方法二,拓扑排序判断是否有环,不关注拓扑排序结果
        bool canFinish_2e(int numCourses, vector<vector<int>> &prerequisites) {
            vector<int> inDegree(numCourses, 0); // 顶点入度
            vector<vector<int>> graph(numCourses, vector<int>()); // 图,邻接表存储
            // 根据先决条件,计算每个点的入度,同时构造图
            for (auto &p  : prerequisites) {
                ++inDegree[p[0]];
                graph[p[1]].push_back(p[0]);
            }

            vector<int> items(numCourses, 0);
            for (int i = 0; i < numCourses; ++i) {
                items[i] = i;
            }
            return topSortJudgeCircle(graph, inDegree, items);
        }

        // 方法三,利用dfs深度优先遍历判断有向图是否存在环
        bool canFinish(int numCourses, const vector<vector<int>> &prerequisites) {
            vector<vector<int>> graph(numCourses, vector<int>()); // 图,邻接表存储
            // 根据先决条件,计算每个点的入度,同时构造图
            for (auto &p  : prerequisites) {
                graph[p[1]].push_back(p[0]);
            }

            // 辅助数据初始化
            visited.assign(numCourses, 0);
            while (!sortRes.empty()) {
                sortRes.pop();
            }
            valid = true;

            // dfs遍历
            for (int i = 0; i < numCourses; ++i) {
                if (visited[i] == 0) {
                    dfs(graph, i);
                    if (!valid) {
                        return false;
                    }
                }
            }
            return true;
        }

    private:
        vector<int> visited; // 三种状态,0未搜索, 1搜索中(邻接节点未搜索完成), 2搜索完
        std::stack<int> sortRes; // 深度优先搜索中利用栈存储拓扑排序结果
        bool valid = true;

        void dfs(const vector<vector<int>> &graph, int v) {
            visited[v] = 1;
            for (int w : graph[v]) {
                if (visited[w] == 1) {
                    // 如果遍历到状态为1的点,则证明该有向图中存在环
                    valid = false;
                    return;
                }
                if (visited[w] == 0) {
                    dfs(graph, w);
                }
            }
            visited[v] = 2;
            sortRes.push(v);
        }
    };
扫描二维码关注公众号,回复: 12239225 查看本文章

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112546120
今日推荐