拓撲排序:對一個有向圖構造拓撲序列的過程。
關鍵概念定義
拓撲序列:設G=(V,E)是一個具有n個頂點的有向圖窄瘟,V中的頂點序列v1,v2睛约,……鼎俘,vn,若滿足從頂點vi到vj有一條路徑辩涝,則在頂點序列中頂點vi必須出現(xiàn)在vj之前贸伐。符合這樣定義的序列叫拓撲序列。
AOV網:在一個表示工程的有向圖中怔揩,用頂點表示活動捉邢,用弧表示活動之間的優(yōu)先關系,這樣的有向圖為頂點表示活動的網商膊,就是AOV網伏伐。
拓撲排序算法思路:從AOV網中選擇一個入度為0的頂點輸出,然后刪除此頂點晕拆,并刪除此頂點為尾的弧藐翎,重復此步驟,直到輸出全部頂點或者AOV網中不存在入度為0的頂點為止实幕。
// 圖的拓撲排序算法
#include <stdio.h>
#include <malloc.h>
#define MAXEDGE 20 // 最大邊數
#define MAXVEX 14 // 最大頂點數
#define OK 1 // 執(zhí)行成功
#define ERROR 0 // 執(zhí)行失敗
typedef int Status; // 執(zhí)行狀態(tài)
// 鄰接矩陣結構
typedef struct {
int vexs[MAXVEX]; // 頂點表
int arc[MAXVEX][MAXVEX]; // 邊表
int numNodes, numEdges; // 圖中當前的頂點數吝镣,邊數
} MGraph;
/**************** 用到的鄰接表結構 ****************/
// 邊表節(jié)點
typedef struct EdgeNode {
int adjvex; // 鄰接點域,存儲該頂點對應的下標
int weight; // 用來存儲權值(非網圖可沒有)
struct EdgeNode *next; // 指向下一個鄰接點
} EdgeNode;
// 頂點表節(jié)點
typedef struct VertexNode {
int in; // 頂點入度
int data; // 頂點域昆庇,存儲頂點信息
EdgeNode *firstEdge; // 邊表頭指針
} VertexNode, AdjList[MAXVEX];
// 鄰接表結構
typedef struct {
AdjList adjList;// 頂點表數組
int numNodes, numEdges; // 圖中當前頂點數末贾,邊數
} graphAdjList, *GraphAdjList;
/*************************************************/
/**
* 生成鄰接矩陣(圖)
* @param G 鄰接矩陣(圖)
*/
void CreateMGraph(MGraph *G) {
int i, j; // 用于遍歷元素
G->numEdges = MAXEDGE; // 設置邊數
G->numNodes = MAXVEX; // 設置頂點數
// 初始化圖的頂點表
for (i = 0; i < G->numNodes; i++) {
G->vexs[i] = i;
}
// 初始化圖的邊表
for (i = 0; i < G->numNodes; i++) {
for (j = 0; j < G->numNodes; j++) {
G->arc[i][j] = 0; // 設置所有邊的默認值為0
}
}
// 設置特定邊的權(值為1表示該位置有邊)
G->arc[0][4] = 1;
G->arc[0][5] = 1;
G->arc[0][11] = 1;
G->arc[1][2] = 1;
G->arc[1][4] = 1;
G->arc[1][8] = 1;
G->arc[2][5] = 1;
G->arc[2][6] = 1;
G->arc[2][9] = 1;
G->arc[3][2] = 1;
G->arc[3][13] = 1;
G->arc[4][7] = 1;
G->arc[5][8] = 1;
G->arc[5][12] = 1;
G->arc[6][5] = 1;
G->arc[8][7] = 1;
G->arc[9][10] = 1;
G->arc[9][11] = 1;
G->arc[10][13] = 1;
G->arc[12][9] = 1;
}
/**
* 使用鄰接矩陣生成鄰接表
* @param G 鄰接矩陣
* @param GL 鄰接表
*/
void CreateALGraph(MGraph G, GraphAdjList *GL) {
int i, j; // 用于遍歷元素
EdgeNode *e; // 邊表節(jié)點
*GL = (GraphAdjList) malloc(sizeof(graphAdjList)); // 為鄰接表表分配存儲空間
(*GL)->numNodes = G.numNodes; // 設置鄰接表的頂點數
(*GL)->numEdges = G.numEdges; // 設置鄰接表的邊數
// 建立頂點表
for (i = 0; i < G.numNodes; i++) {
(*GL)->adjList[i].in = 0; // 設置下標為i位置的頂點入讀為0
(*GL)->adjList[i].data = G.vexs[i]; // 存入該頂點的權值
(*GL)->adjList[i].firstEdge = NULL; // 設置頂點對應的邊表為空
}
// 建立邊表
for (i = 0; i < G.numNodes; i++) {
for (j = 0; j < G.numNodes; j++) {
if (G.arc[i][j] == 1) {
e = (EdgeNode *) malloc(sizeof(EdgeNode)); // 為邊表節(jié)點分配存儲空間
e->adjvex = j; // 設置鄰接序號為j
e->next = (*GL)->adjList[i].firstEdge; // 新節(jié)點指針指向邊表的頭節(jié)點
(*GL)->adjList[i].firstEdge = e; // 讓新節(jié)點成為邊表的頭節(jié)點
(*GL)->adjList[j].in++; // 邊終點所指向的節(jié)點入度加1
}
}
}
}
/**
* 拓撲排序
* 若鄰接表無回路,則輸出拓撲排序序列并返回OK整吆,若有回路則返回ERROR
* @param GL 鄰接表
* @return 執(zhí)行狀態(tài)
*/
Status TopologicalSort(GraphAdjList GL) {
EdgeNode *e; // 邊表節(jié)點
int i, k, gettop; // i用于遍歷元素拱撵,k用來獲取頂點下標,gettop用來獲取棧頂元素下標
int top = 0; // 用于棧指針下標
int count = 0; // 用于統(tǒng)計輸出頂點的個數
int *stack; // 存儲入度為0的頂點的棧
stack = (int *) malloc(GL->numNodes * sizeof(int)); // 為棧分配存儲空間
// 將所有入度為0的頂點入棧
for (i = 0; i < GL->numNodes; i++) {
if (GL->adjList[i].in == 0) { // 頂點入度為0
stack[++top] = i; // 將該節(jié)點入棧
}
}
// 未把所有入度為0的頂點出棧
while (top != 0) {
gettop = stack[top--]; // 獲取入度為0的棧的棧頂元素
printf("%d -> ", GL->adjList[gettop].data); // 打印該元素的頂點信息
count++; // 輸入頂點個數加1
// 遍歷該頂點的邊表
for (e = GL->adjList[gettop].firstEdge; e; e = e->next) {
k = e->adjvex; // 獲取下個頂點(邊終點所指向的節(jié)點)的下標
// 將邊終點所指向的節(jié)點的入度減1表蝙,如果該頂點入讀為0拴测,將頂點入棧
if (!(--GL->adjList[k].in)) {
stack[++top] = k; // 將頂點入棧
}
}
}
printf("\n");
// 輸出的頂點數小于鄰接表的頂點數,鄰接表中有回路府蛇,拓撲排序失敗
if (count < GL->numNodes) {
return ERROR;
} else { // 鄰接表中無回路集索,拓撲排序成功
return OK;
}
}
int main() {
MGraph G; // 鄰接矩陣
GraphAdjList GL; // 鄰接表
Status status; // 執(zhí)行狀態(tài)
CreateMGraph(&G); // 創(chuàng)建鄰接矩陣
CreateALGraph(G, &GL); // 使用鄰接矩陣創(chuàng)建鄰接表
status = TopologicalSort(GL); // 將鄰接表進行拓撲排序,獲取執(zhí)行狀態(tài)
printf("status: %s", status == OK ? "執(zhí)行成功" : "執(zhí)行失敗");
return 0;
}