简介
给定一个包含 n 个节点的有向图 G ,我们给出它的节点编号的一种排列,如果满足:
对于图 G 中的任意一条有向边 (u, v),u 在排列中都出现在 v 的前面。
那么称该排列是图 G 的「拓扑排序」。根据上述的定义,我们可以得出两个结论:
- 如果图 G 中存在环,那么不存在拓扑排序
- 拓扑排序可能不止一种
代码实现
结合LeetCode的一道题目来实现拓扑排序的代码:课程表
方法一:广度优先搜索
思路:找出入度为0的结点,删去他引出的边,将他相邻的新的入度为0的点加入队列,重复此操作。
时间复杂度为:O(n + m) (n为结点数,m为有向边数)
using System;
using System.Collections.Generic;
public class Solution {
// 链式前向星
struct Edge {
public int dest, next;
}
int n;
int edgesCnt = 0;
Edge[] edges = new Edge[100000];
int[] head = new int[100000];
// 入度
int[] inDegrees = new int[100000];
void AddEdge(int src, int dest) {
++edgesCnt;
edges[edgesCnt].dest = dest;
edges[edgesCnt].next = head[src];
head[src] = edgesCnt;
++inDegrees[dest];
}
int[] TopoSort() {
int[] res = new int[n];
Queue<int> q = new Queue<int>();
// 将入度为0的结点加入队列中
for (int i = 0; i < n; ++i) {
if (inDegrees[i] == 0) {
q.Enqueue(i);
}
}
// 广度优先搜索
int resCount = 0;
while (q.Count > 0) {
int node = q.Dequeue();
res[resCount++] = node;
for (int i = head[node]; i > 0; i = edges[i].next) {
int dest = edges[i].dest;
--inDegrees[dest];
if (inDegrees[dest] == 0) {
q.Enqueue(dest);
}
}
}
if (resCount == n) {
return res;
} else {
return new int[0];
}
}
public int[] FindOrder(int numCourses, int[][] prerequisites) {
n = numCourses;
foreach (int[] edge in prerequisites) {
AddEdge(edge[1], edge[0]);
}
return TopoSort();
}
}
方法二:深度优先搜索
思路:对于一个节点 u,如果它的所有相邻节点都已经搜索完成,那么在搜索回溯到 u 的时候,u 本身也会变成一个已经搜索完成的节点,将 u 入栈。全部搜索完毕后依次出栈即可。
时间复杂度:O(n + m) (n为结点数,m为有向边数)
using System;
using System.Collections.Generic;
public class Solution {
// 链式前向星
struct Edge {
public int dest, next;
}
int n;
int edgesCnt = 0;
Edge[] edges = new Edge[100000];
int[] head = new int[100000];
void AddEdge(int src, int dest) {
++edgesCnt;
edges[edgesCnt].dest = dest;
edges[edgesCnt].next = head[src];
head[src] = edgesCnt;
}
// visited数组,用于DFS
// 0:未搜索 1:已搜索 -1:正在搜索
int[] visited = new int[100000];
// 存放搜索序列
Stack<int> sequence = new Stack<int>();
bool DFS(int node) {
// 将结点标记为正在搜索
visited[node] = -1;
for (int i = head[node]; i > 0; i = edges[i].next) {
int dest = edges[i].dest;
if (visited[dest] == -1) { // 有环
return false;
} else if (visited[dest] == 0 && !DFS(dest)) {
return false;
}
}
// 搜索完后标记为已搜索
visited[node] = 1;
sequence.Push(node);
return true;
}
public int[] FindOrder(int numCourses, int[][] prerequisites) {
n = numCourses;
foreach (int[] edge in prerequisites) {
AddEdge(edge[1], edge[0]);
}
for (int i = 0; i < n; ++i) {
if (visited[i] == 0) {
if (!DFS(i)) {
return new int[0];
}
}
}
if (sequence.Count == n) {
int[] res = new int[n];
for (int i = 0; i < n; ++i) {
res[i] = sequence.Pop();
}
return res;
} else {
return new int[0];
}
}
}