定义
给定一个包含 n 个节点的有向图 G ,我们给出它的节点编号的一种排列,如果满足:
对于图 G 中的任意一条有向边 (u, v),u 在排列中都出现在 v 的前面。
那么称该排列是图 G 的「拓扑排序」。根据上述的定义,我们可以得出两个结论:
- 如果图 G 中存在环,那么不存在拓扑排序。
- 拓扑排序可能不止一种。
拓扑排序的时间复杂度为:O(N + M)
方法一:深度优先搜索
LeetCode 210
思路:对于一个节点 u,如果它的所有相邻节点都已经搜索完成,那么在搜索回溯到 u 的时候,u 本身也会变成一个已经搜索完成的节点,将 u 入栈。全部搜索完毕后依次出栈即可。
时间复杂度:O(N + M)
#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <map>
#include <set>
#include <cstring>
#include <climits>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <cassert>
using namespace std;
const int MAXN = 100005;
const int MAXM = 100005;
struct Edge {
int dest, next;
} edges[MAXM];
int edges_cnt, n;
int head[MAXN];
vector<int> topo_res;
// visited数组,用于dfs
// 0:未搜索 1:已搜索 -1:正在搜索
char visited[MAXN];
void addEdge(int src, int dest) {
++edges_cnt;
edges[edges_cnt].dest = dest;
edges[edges_cnt].next = head[src];
head[src] = edges_cnt;
}
bool dfs(int node) {
visited[node] = -1;
for (int i = head[node]; i; i = edges[i].next) {
int dest = edges[i].dest;
if (visited[dest] == -1) { // 存在环
return false;
} else if (!visited[dest] && !dfs(dest)) {
return false;
}
}
visited[node] = 1;
topo_res.emplace_back(node);
return true;
}
bool topo_sort() {
topo_res.clear();
for (int node = 0; node < n; ++node) {
if (!visited[node] && !dfs(node)) {
return false;
}
}
reverse(topo_res.begin(), topo_res.end());
return true;
}
class Solution {
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges_cnt = 0;
n = numCourses;
memset(head, 0, sizeof(head));
memset(visited, 0, sizeof(visited));
for (auto& edge : prerequisites) {
addEdge(edge[1], edge[0]);
}
if (topo_sort())
return topo_res;
else
return {};
}
};
方法二:广度优先搜索
思路:找出入度为0的结点,删去他引出的边,将他相邻的新的入度为0的点加入队列,重复此操作。
时间复杂度:O(N + M)
#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <map>
#include <set>
#include <cstring>
#include <climits>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <cassert>
using namespace std;
const int MAXN = 100005;
const int MAXM = 100005;
struct Edge {
int dest, next;
} edges[MAXM];
int edges_cnt, n;
int head[MAXN];
int in_degrees[MAXN];
vector<int> topo_res;
void addEdge(int src, int dest) {
++in_degrees[dest];
++edges_cnt;
edges[edges_cnt].dest = dest;
edges[edges_cnt].next = head[src];
head[src] = edges_cnt;
}
bool topo_sort() {
queue<int> q;
for (int node = 0; node < n; ++node) {
if (!in_degrees[node]) {
q.emplace(node);
}
}
topo_res.clear();
while (!q.empty()) {
int top = q.front();
q.pop();
topo_res.emplace_back(top);
for (int i = head[top]; i; i = edges[i].next) {
int dest = edges[i].dest;
--in_degrees[dest];
if (!in_degrees[dest]) {
q.emplace(dest);
}
}
}
return topo_res.size() == n;
}
class Solution {
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges_cnt = 0;
n = numCourses;
memset(head, 0, sizeof(head));
memset(in_degrees, 0, sizeof(in_degrees));
for (auto& edge : prerequisites) {
addEdge(edge[1], edge[0]);
}
if (topo_sort())
return topo_res;
else
return {};
}
};