簡介
給定一個包含 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];
}
}
}