简介
拓扑排序是一种针对有序无环图的排序方法,其会将一个有序无环图转化为一条链,且对于有序无环图中的每一条边u -> v
,都保证节点u
在节点v
之前出现,即在将原图转化为一条链的过程中不破坏节点的先后顺序。

可以看到,上图中的链中节点的先后顺序仍然保持原样。
需要注意的是:一个有环的图是没法进行拓扑排序的,这很好理解,因为u -> v
和v -> u
的路径同时出现的话,u
和v
在链内就没有任何一种合法的排序方法,自然也无法排序。
前置知识:度
顶点的度是指某个顶点连出的边数。特别地,在有向图中,入度是指以该顶点为终点的有向边数量;顶点的出度是指以顶点为起点的有向边数量。

上图展示了一个入度为2,出度为3的点
同时,很容易知道,在一个有向图中所有节点的入度之和和出度之和相等。
算法实现(C++)
以LeetCode上第210题【课程表II】为例,根据题面信息,我们可以知道这是一道实现拓扑排序的题目。

实现拓扑排序的一个基本思路是使用广度优先搜索,其基本流程和思路如下:
- 求解每个顶点的入度并储存
- 开始循环,首先将当前状态下所有入度为
0
的点加入链中
由于这样的点没有任何指向于它的边存在,故将其加入链必然不与节点之间的先后顺序矛盾。
- 将刚才加入了链的点在图中删除,减少与其有关联的点的入度
因为这个点已经加入了链,故在它之后加入的点相对于它而言就一定是合法的,故在图中取消这个点对接下来其它节点的限制。
- 回到循环,检查在刚才的操作中入度变为
0
的节点,再执行相同的操作
- 直到发现所有节点都被添加入链或剩下的节点没有一个入度为
0
(图带环),结束循环,返回结果
以下是一张示例图,展示了用BFS实现拓扑排序的一个进行过程。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
#include <vector> #include <queue> #include <unordered_map> using namespace std;
class Solution { public: vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) { vector<int> indegrees; indegrees.resize(numCourses, 0); unordered_map<int, vector<int>> links; for (int i = 0; i < prerequisites.size(); i++) { indegrees[prerequisites[i][0]]++; if (links.find(prerequisites[i][1]) == links.end()) { links.insert(pair<int, vector<int>>(prerequisites[i][1], vector<int>())); } links[prerequisites[i][1]].push_back(prerequisites[i][0]); } vector<int> result; queue<int> openNodes; while (true) { for (int i = 0; i < indegrees.size(); i++) { if (!indegrees[i]) { result.push_back(i); openNodes.push(i); indegrees[i] = -1; } } if (openNodes.empty()) { if (result.size() == numCourses) { return result; } else { return vector<int>(); } } while (!openNodes.empty()) { vector<int> connected = links[openNodes.front()]; for (int node : connected) { indegrees[node]--; } openNodes.pop(); } } } };
|
后记
以上就是拓扑排序的大致介绍,在实际情况中,它常用来解决关键路径问题(AOE网络),在此不作详细展开。
总之感谢您的阅读!