當每個任務有前後置關係時,需要找到一種滿足前後置關係的路線,將任務完成。
如果將每個任務看成一個節點,任務之間的前後置關係表示爲有向圖時,這種路線順序叫做爲圖進行拓撲排序。也叫關鍵路徑分析。
這裏的圖用鄰接矩陣法表示,算法的關鍵是:
- 找到一個沒有後繼的頂點 ;
- 在圖中刪除它,放入結果數組中 ;
- 重複 步驟 1 ,步驟 2 直到圖中沒有多餘的節點;
代碼:
/**
* @ClassName Node
* @Description 圖節點
* @Author lzq
* @Date 2019/6/19 04:39
* @Version 1.0
**/
public class Node {
public char label; //存放的數據
public boolean wasVisited; //記錄有無被訪問過
public Node(char label) {
this.label = label;
this.wasVisited = false;
}
}
/**
* @ClassName Graph3
* @Description 拓撲排序
* @Author lzq
* @Date 2019/6/19 06:49
* @Version 1.0
**/
public class Graph3 {
private final int MAX_VERTS = 20; //表示一個圖節點能連接的最大定點數
private Node[] nodeList; //頂點數組
private int[][] adjMat; //鄰接矩陣,用來存方節點之間關係的
private int nNode; //當前頂點數量
private char[] sorteArray; //存放拓撲排序之後字符數組
public Graph3() {
nodeList = new Node[MAX_VERTS];
adjMat = new int[MAX_VERTS][MAX_VERTS];
nNode = 0;
for (int i = 0; i < MAX_VERTS; i++) {
for (int j = 0; j < MAX_VERTS; j++) {
adjMat[i][j] = 0;
}
}
sorteArray = new char[MAX_VERTS];
}
/**
* 添加節點
* @param lab
*/
public void addNode(char lab) {
nodeList[nNode++] = new Node(lab);
}
/**
* 添加邊
* @param start
* @param end
*/
public void addEdge(int start,int end) {
adjMat[start][end] = 1;
}
/* *//**
* 打印頂點
* @param v
*//*
public void show(int v) {
System.out.print(nodeList[v].label+"\t");
}*/
/**
* 拓撲排序
*/
public void topo() {
int o = nNode;
while (nNode > 0) { //把所有頂點從圖裏面刪除
int c = noSuccessors();
if(c == -1) { //找不到沒有後繼頂點的頂點
System.out.println("錯誤!!!");
return;
}
sorteArray[nNode-1] = nodeList[c].label;
deleteNode(c); //從圖中刪除當前頂點
}
System.out.print("拓撲排序:");
for (int i = 0; i < o; i++) {
System.out.print(sorteArray[i]);
}
}
private void deleteNode(int del) {
if(del != nNode-1){ //要刪除的頂點不是最後一個,就要處理鄰接矩陣
for (int i = del; i < nNode-1; i++) { //刪除頂點後面元素向前移動
nodeList[i] = nodeList[i+1];
}
for (int i = del; i < nNode-1; i++) { //把鄰接矩陣中,刪除後面的行向上移動
moveRowUp(i,nNode);
}
for (int col = del; col < nNode-1; col++) {
moveColLeft(col,nNode-1);
}
}
nNode--; //數量遞減
}
/**
* 矩陣行向上移
* @param row
* @param length
*/
private void moveRowUp(int row,int length) {
for (int i = 0; i < length; i++) {
adjMat[row][i] = adjMat[row+1][i];
}
}
/**
* 列往左移
* @param col
* @param length
*/
private void moveColLeft(int col,int length) {
for (int row = 0; row < length; row++) {
adjMat[row][col] = adjMat[row][col+1];
}
}
/**
* 查找途中沒有後繼頂點的頂點
* @return
*/
private int noSuccessors() {
boolean flag = true;
for (int i = 0; i < nNode; i++) {
flag = false;
for (int j = 0; j < nNode; j++) {
if (adjMat[i][j] > 0) {
flag = true;
break;
}
}
if(!flag) {
return i;
}
}
return -1;
}
}
/**
* @ClassName TestDemo1
* @Description 拓撲排序測試
* @Author lzq
* @Date 2019/6/19 07:21
* @Version 1.0
**/
public class TestDemo1 {
public static void main(String[] args) {
Graph3 graph3 = new Graph3();
graph3.addNode('A');
graph3.addNode('B');
graph3.addNode('C');
graph3.addNode('D');
graph3.addNode('E');
graph3.addNode('F');
graph3.addNode('G');
graph3.addNode('H');
graph3.addEdge(0,3);
graph3.addEdge(0,4);
graph3.addEdge(1,4);
graph3.addEdge(2,5);
graph3.addEdge(3,6);
graph3.addEdge(4,6);
graph3.addEdge(5,7);
graph3.addEdge(6,7);
graph3.topo();
}
}
測試代碼表示的圖:
運行結果:
拓撲排序:BAEDGCFH