一、定義
對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。
二、DFS算法
找出一個入度爲 0 的頂點,通過遞歸的方式遍歷它所有可達的頂點,將其輸出到拓撲排序的結果序列中,對遍歷過的頂點做標記,避免其被重複訪問。循環執行上面的過程,直到所有的頂點都被輸出。
1.基於鄰接表實現
public class TopologicalSort_AdjacencyList_Dfs {
private int mVertexNum; // 頂點數
private int[] mInDegree; // 頂點入度數
private List<Integer> mAdjacencyList[]; // 鄰接表
private boolean[] isVisited; // 用來避免頂點被重複訪問
private Stack<Integer> mReversePost; // 用棧來保存排序結果
public TopologicalSort_AdjacencyList_Dfs(int vertexNum) {
this.mVertexNum = vertexNum;
this.mInDegree = new int[vertexNum];
this.mAdjacencyList = new LinkedList[vertexNum];
for (int i = 0; i < vertexNum; ++i) {
mAdjacencyList[i] = new LinkedList<>();
}
this.isVisited = new boolean[vertexNum];
this.mReversePost = new Stack<>();
}
/**
* 添加圖的邊,累計終止頂點入度
*
* @param s 起始頂點
* @param t 終止頂點
*/
public void addEdge(int s, int t) {
mAdjacencyList[s].add(t);
mInDegree[t]++;
}
/**
* 拓撲排序
*/
public void sort() {
for (int i = 0; i < mVertexNum; i++) {
if (mInDegree[i] == 0) {
recurDfs(i);
}
}
System.out.println(mReversePost);
}
/**
* 遞歸方法
*
* @param s
*/
private void recurDfs(int s) {
if (isVisited[s] || mReversePost.size() == mVertexNum) {
return;
}
isVisited[s] = true;
mReversePost.push(s);
for (int i = 0; i < mAdjacencyList[s].size(); i++) {
int w = mAdjacencyList[s].get(i);
if (isVisited[w]) {
return;
}
mInDegree[w]--;
if (mInDegree[w] == 0) {
recurDfs(w);
}
}
}
public static void main(String[] args) {
TopologicalSort_AdjacencyList_Dfs graph = new TopologicalSort_AdjacencyList_Dfs(8);
graph.addEdge(0, 1);
graph.addEdge(0, 3);
// graph.addEdge(1, 2);
graph.addEdge(1, 4);
graph.addEdge(2, 5);
graph.addEdge(4, 5);
graph.addEdge(4, 6);
graph.addEdge(5, 7);
graph.addEdge(6, 7);
graph.sort();
}
}
2.基於鄰接矩陣實現
public class TopologicalSort_AdjacencyMatrix_Dfs {
private int mVertexNum; // 頂點數
private int[] mInDegree; // 頂點入度數
private int[][] mAdjacencyMatrix; // 鄰接矩陣
private boolean[] isVisited; // 用來避免頂點被重複訪問
private Stack<Integer> mReversePost; // 用棧來保存排序結果
public TopologicalSort_AdjacencyMatrix_Dfs(int vertexNum) {
this.mVertexNum = vertexNum;
this.mInDegree = new int[vertexNum];
this.mAdjacencyMatrix = new int[vertexNum][vertexNum];
for (int i = 0; i < vertexNum; i++) {
for (int j = 0; j < vertexNum; j++) {
mAdjacencyMatrix[i][j] = 0;
}
}
this.isVisited = new boolean[vertexNum];
this.mReversePost = new Stack<>();
}
/**
* 添加圖的邊,累計終止頂點入度
*
* @param s 起始頂點
* @param t 終止頂點
*/
public void addEdge(int s, int t) {
mAdjacencyMatrix[s][t] = 1;
mInDegree[t]++;
}
/**
* 拓撲排序
*/
public void sort() {
for (int i = 0; i < mVertexNum; i++) {
if (mInDegree[i] == 0) {
recurDfs(i);
}
}
System.out.println(mReversePost);
}
/**
* 遞歸方法
*
* @param s
*/
private void recurDfs(int s) {
if (isVisited[s] || mReversePost.size() == mVertexNum) {
return;
}
isVisited[s] = true;
mReversePost.push(s);
for (int i = 0; i < mVertexNum; i++) {
if (mAdjacencyMatrix[s][i] == 1 && !isVisited[i]) {
mInDegree[i]--;
if (mInDegree[i] == 0) {
recurDfs(i);
}
}
}
}
public static void main(String[] args) {
TopologicalSort_AdjacencyMatrix_Dfs graph = new TopologicalSort_AdjacencyMatrix_Dfs(8);
graph.addEdge(0, 1);
graph.addEdge(0, 3);
// graph.addEdge(1, 2);
graph.addEdge(1, 4);
graph.addEdge(2, 5);
graph.addEdge(4, 5);
graph.addEdge(4, 6);
graph.addEdge(5, 7);
graph.addEdge(6, 7);
graph.sort();
}
}
三、Kahn算法
找出一個入度爲 0 的頂點,將其輸出到拓撲排序的結果序列中,並且把這個頂點從圖中刪除。循環執行上面的過程,直到所有的頂點都被輸出。
1.基於鄰接表實現
public class TopologicalSort_AdjacencyList_Kahn {
private int mVertexNum; // 頂點數
private int[] mInDegree; // 頂點入度數
private List<Integer> mAdjacencyList[]; // 鄰接表
private Deque<Integer> mZeroDegreeDeque; // 入度數爲0的頂點
public TopologicalSort_AdjacencyList_Kahn(int vertexNum) {
this.mVertexNum = vertexNum;
this.mInDegree = new int[vertexNum];
this.mAdjacencyList = new LinkedList[vertexNum];
for (int i = 0; i < vertexNum; ++i) {
mAdjacencyList[i] = new LinkedList<>();
}
this.mZeroDegreeDeque = new ArrayDeque<>();
}
/**
* 添加圖的邊,累計終止頂點入度
*
* @param s 起始頂點
* @param t 終止頂點
*/
public void addEdge(int s, int t) {
mAdjacencyList[s].add(t);
mInDegree[t]++;
}
/**
* 拓撲排序
*/
public void sort() {
for (int i = 0; i < mVertexNum; i++) {
if (mInDegree[i] == 0) {
mZeroDegreeDeque.offer(i);
}
}
List<Integer> result = new ArrayList<>();
while (!mZeroDegreeDeque.isEmpty()) {
int s = mZeroDegreeDeque.poll();
for (int j = 0; j < mAdjacencyList[s].size(); j++) {
int w = mAdjacencyList[s].get(j);
mInDegree[w]--;
if (mInDegree[w] == 0) {
mZeroDegreeDeque.offer(w);
}
}
result.add(s);
}
System.out.println(result);
}
public static void main(String[] args) {
TopologicalSort_AdjacencyList_Kahn graph = new TopologicalSort_AdjacencyList_Kahn(8);
graph.addEdge(0, 1);
graph.addEdge(0, 3);
graph.addEdge(1, 2);
graph.addEdge(1, 4);
graph.addEdge(2, 5);
graph.addEdge(4, 5);
graph.addEdge(4, 6);
graph.addEdge(5, 7);
graph.addEdge(6, 7);
graph.sort();
}
}
2.基於鄰接矩陣實現
public class TopologicalSort_AdjacencyMatrix_Kahn {
private int mVertexNum; // 頂點數
private int[] mInDegree; // 頂點入度數
private int[][] mAdjacencyMatrix; // 鄰接矩陣
private Deque<Integer> mZeroDegreeDeque; // 入度數爲0的頂點
public TopologicalSort_AdjacencyMatrix_Kahn(int vertexNum) {
this.mVertexNum = vertexNum;
this.mInDegree = new int[vertexNum];
this.mAdjacencyMatrix = new int[vertexNum][vertexNum];
for (int i = 0; i < vertexNum; i++) {
for (int j = 0; j < vertexNum; j++) {
mAdjacencyMatrix[i][j] = 0;
}
}
this.mZeroDegreeDeque = new ArrayDeque<>();
}
/**
* 添加圖的邊,累計終止頂點入度
*
* @param s 起始頂點
* @param t 終止頂點
*/
public void addEdge(int s, int t) {
mAdjacencyMatrix[s][t] = 1;
mInDegree[t]++;
}
/**
* 拓撲排序
*/
public void sort() {
for (int i = 0; i < mVertexNum; i++) {
if (mInDegree[i] == 0) {
mZeroDegreeDeque.offer(i);
}
}
List<Integer> result = new ArrayList<>();
while (!mZeroDegreeDeque.isEmpty()) {
int s = mZeroDegreeDeque.poll();
for (int j = 0; j < mVertexNum; j++) {
if (mAdjacencyMatrix[s][j] != 1) {
continue;
}
mInDegree[j]--;
if (mInDegree[j] == 0) {
mZeroDegreeDeque.offer(j);
}
}
result.add(s);
}
System.out.println(result);
}
public static void main(String[] args) {
TopologicalSort_AdjacencyMatrix_Kahn graph = new TopologicalSort_AdjacencyMatrix_Kahn(8);
graph.addEdge(0, 1);
graph.addEdge(0, 3);
graph.addEdge(1, 2);
graph.addEdge(1, 4);
graph.addEdge(2, 5);
graph.addEdge(4, 5);
graph.addEdge(4, 6);
graph.addEdge(5, 7);
graph.addEdge(6, 7);
graph.sort();
}
}