LeetCode-210. Course Schedule II

description

Now you have a total of n courses to choose, denoted as 0 to n-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, return to the order of study you arranged to complete all courses.

There may be more than one correct order, you only need to return one. If it is impossible to complete all courses, return an empty array.

Example 1:

Input: 2, [[1,0]] 
Output: [0,1]
Explanation: There are 2 courses in total. To study course 1, you need to complete course 0 first. Therefore, the correct course order is [0,1].
Example 2:

Input: 4, [[1,0],[2,0],[3,1],[3,2]]
Output: [0,1,2,3] or [0,2,1,3]
Explanation: There are 4 courses in total. To study Course 3, you should complete Course 1 and Course 2 first. And both Course 1 and Course 2 should be ranked after Course 0.
     Therefore, a correct course order is [0,1,2,3]. Another correct order is [0,2,1,3].
Description:

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.
prompt:

This problem is equivalent to finding whether a cycle exists in a directed graph. If there is a loop, there is no topological sorting, so it is impossible to select all courses for learning.
Topological Sorting via DFS-A wonderful video tutorial (21 minutes) on Coursera that introduces the basic concepts of topological sorting.
Topological sorting can also be done through BFS.

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

 

Solve

    class Solution {
    public:
        // 方法一,通过dfs遍历将数据入栈得到拓扑排序结果
        vector<int> findOrder_1e(int numCourses, 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遍历
            vector<int> res;
            for (int i = 0; i < numCourses; ++i) {
                if (visited[i] == 0) {
                    dfs(graph, i);
                    if (!valid) {
                        return res; // 如果图中存在环,则不可能有拓扑排序,直接返回空
                    }
                }
            }

            // 通过栈中的数据得到拓扑排序
            while (!sortRes.empty()) {
                res.emplace_back(sortRes.top());
                sortRes.pop();
            }
            return res;
        }

        // 方法二,直接进行拓扑排序,即每次选取入度为0的数据插入队列或者栈,并且更新相应邻接点的入度,如果为0则放入容器,一直循环直到结束
        // 校验容器中的数据是否为顶点全集,是表明存在拓扑排序,否则有向图中有环
        vector<int> findOrder_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;
            }

            auto res = topSort_useStack(graph, inDegree, items);
            return res.size() == numCourses ? res : vector<int>();
        }

        // 方法三,直接拓扑排序,用队列实现
        vector<int> findOrder(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]);
            }

            auto res = topSort_useQueue(graph, inDegree);
            return res.size() == numCourses ? res : vector<int>();
        }

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

        // 深度遍历dfs
        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);
        }

        /*!
 *
 * @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;
        }

        /*!
 *
 * @param graph 图,邻接表
 * @param inDegree 顶点入度
 * @param items 待排序顶点
 * @return 拓扑排序结果
 */
        vector<int>
        topSort_useQueue(const vector<vector<int>> &graph, vector<int> &inDegree) {
            // 找出入度为0的点开始遍历
            std::queue<int> s;
            const int n = graph.size();
            for (int i = 0; i < n; ++i) {
                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;
        }
    };

 

Guess you like

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