LeetCode-207. Course Schedule

description

You must take numCourse as an elective course this semester, which is recorded as 0 to numCourse-1.

Some prerequisite courses are required before taking certain courses. For example, if you want to study course 0, you need to complete course 1 first. We use a match to represent them: [0,1]

Given the total number of courses and their prerequisites, please judge whether it is possible to complete all courses?

 

Example 1:

Input: 2, [[1,0]] 
Output: true
Explanation: There are 2 courses in total. Before taking course 1, you need to complete course 0. So it is possible.
Example 2:

Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are 2 courses in total. Before studying course 1, you need to complete course 0; and before studying course 0, you should also complete course 1. This is impossible.
 

prompt:

The prerequisite for input is the graph represented by the edge list, not the adjacency matrix. See the representation of the diagram for details.
You can assume that there are no duplicate edges in the preconditions entered.
1 <= numCourses <= 10^5

Source: LeetCode
Link: https://leetcode-cn.com/problems/course-schedule/

 

Solve

   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);
        }
    };

 

Guess you like

Origin blog.csdn.net/u010323563/article/details/112546120