原题地址:
https://leetcode-cn.com/problems/course-schedule-ii/
分析:
本题是一道经典的「拓扑排序」问题。
给定一个包含n个节点的有向图G,我们给出它的节点编号的一种排列,如果满足:对于图G中的任意一条有向边(u,v),u在排列中都出现在v的前面。
那么称该排列是图G的 拓扑排序。可以得到两个结论:
1、如果图G中存在环,那么图G不存在拓扑排序。
2、如果图G是有向无环图,那么它的拓扑排序可能不止一种。极端的例子就是:如果图G值包含n个节点没有边,则任意编号排列都可以作为拓扑排序。
广度优先搜索排列:
思路:最先被放在拓扑排序中节点一定是 这个节点 没有任何入边。然后当一个节点被放在拓扑排序中后,这个节点指向的所有的入边个数都会减少一。直到所有的节点中没有入边的节点(此时可能会存在环)。
算法:使用一个队列来进行广度优先搜索。开始时,所有入度为0的节点都被放入队列中,它们就是可以作为拓扑排序最前面的节点,并且他们之间的相对顺序是无关紧要的。
在广度优先搜索的每一步中,我们取出队首的节点u:
(1)将u放在拓扑排序中;
(2)移除u的所有出边,也就是将u的所有相邻节点的入度减少1。如果相邻节点v的入度变为0,那么我们将v放入队列中。
最后如果拓扑排序中包含这n个节点,则这个就是我们要找的拓扑排序。否则说明存在环。
class Solution { private: vector<vector<int>> edges; vector<int> indeg; vector<int> result; public: vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) { edges.resize(numCourses); indeg.resize(numCourses); for(const auto& info : prerequisites) { ++indeg[info[0]]; edges[info[1]].push_back(info[0]); } queue<int> q; for (int i = 0; i < numCourses; ++i) { if (indeg[i] == 0) { q.push(i); } } while(!q.empty()) { int u = q.front(); q.pop(); result.push_back(u); for (int v : edges[u]) { --indeg[v]; if (indeg[v] == 0) { q.push(v); } } } if (result.size() != numCourses) { return {}; } return result; } };