在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關系,這樣的有向圖為頂點表示活動的網,我們稱之為AOV網(Activity on Vextex Network)。AOV網中的弧表示活動之間存在的某種制約關系,AOV網中不能存在回路,讓某個活動的開始要以自己完成作為先決條件,顯然是不可以的。
設G= { V, E }是一個具有n個頂點的有向圖,V中的頂點序列v1, v2, ...,vn,滿足若從頂點vi到vj有一條路徑,則在頂點序列中頂點vi必在vj之前,則我們稱這樣的頂點序列為一個拓撲排序。
所謂拓撲排序,其實就是對一個有向圖構造拓撲序列的過程。構造時會有兩個結果,如果此網的全部頂點都被輸出,則說明它是不存在(回路)的AOV網;如果輸出頂點少了,哪怕是少了一個,也說明這個網存在環路,不是AOV網。
對AOV網進行拓撲排序的基本思路是:從AOV網中選擇一個入度為0的頂點輸出,然后刪去此頂點,並刪除以此頂點為尾的弧,繼續重復此步驟,直到輸出全部頂點或者AOV網中不存在入度為0的頂點為止。
由於在拓撲排序的過程中,需要刪除頂點,顯然用鄰接表的結構會更加方便,考慮到算法中始終要查找入度為0的頂點,我們可以在原來頂點表結點結構中,增加一個入度域in, 即入度的數字,上面所提到的刪除以某個頂點為尾的弧的操作也是通過將某頂點的鄰接點的in減去1,表示刪除了中間連接的弧。
對於圖7-8-2的第一幅圖AOV網,可以得到如第二幅圖的鄰接表數據結構。
另外,在算法中,還需要輔助的數據結構--棧,用來存儲處理過程中入度為0的點,目的是為了避免每次查找時都要去遍歷頂點表找有沒有入度為0的頂點。
下面來看整體代碼(改編自《大話數據結構》)
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
#include<iostream> using namespace std; #define MAXEDGE 20 #define MAXVEX 14 #define INFINITY 65535 /* 鄰接矩陣結構 */ typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; } MGraph; /* 鄰接表結構****************** */ typedef struct EdgeNode /* 邊表結點 */ { int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */ int weight; /* 用於存儲權值,對於非網圖可以不需要 */ struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */ } EdgeNode; typedef struct VertexNode /* 頂點表結點 */ { int in; /* 頂點入度 */ int data; /* 頂點域,存儲頂點信息 */ EdgeNode *firstedge;/* 邊表頭指針 */ } VertexNode, AdjList[MAXVEX]; typedef struct { AdjList adjList; int numVertexes, numEdges; /* 圖中當前頂點數和邊數 */ } graphAdjList, *GraphAdjList; /* **************************** */ void CreateMGraph(MGraph *G)/* 構建圖 */ { int i, j; /* printf("請輸入邊數和頂點數:"); */ G->numEdges = MAXEDGE; G->numVertexes = MAXVEX; for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { G->vexs[i] = i; } for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { G->arc[i][j] = 0; } } 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; } /* 利用鄰接矩陣構建鄰接表 */ void CreateALGraph(MGraph G, GraphAdjList *GL) { int i, j; EdgeNode *e; *GL = (GraphAdjList)malloc(sizeof(graphAdjList)); (*GL)->numVertexes = G.numVertexes; (*GL)->numEdges = G.numEdges; for(i = 0; i < G.numVertexes; i++) /* 讀入頂點信息,建立頂點表 */ { (*GL)->adjList[i].in = 0; (*GL)->adjList[i].data = G.vexs[i]; (*GL)->adjList[i].firstedge = NULL; /* 將邊表置為空表 */ } for(i = 0; i < G.numVertexes; i++) /* 建立邊表 */ { for(j = 0; j < G.numVertexes; j++) { if (G.arc[i][j] == 1) { e = (EdgeNode *)malloc(sizeof(EdgeNode)); e->adjvex = j; /* 鄰接序號為j */ e->next = (*GL)->adjList[i].firstedge; /* 將當前頂點上的指向的結點指針賦值給e */ (*GL)->adjList[i].firstedge = e; /* 將當前頂點的指針指向e */ (*GL)->adjList[j].in++; /* 注意這里是j */ } } } } /* 拓撲排序,若GL無回路,則輸出拓撲排序序列並返回1,若有回路返回0。 */ bool TopologicalSort(GraphAdjList GL) { EdgeNode *pe; int i, k, gettop; int top = 0;/* 用於棧指針下標 */ int count = 0;/* 用於統計輸出頂點的個數 */ /* 建棧將入度為0的頂點入棧 */ int *stack = (int *)malloc(sizeof(GL->numVertexes * sizeof(int))); for (i = 0; i < GL->numVertexes; i++) if (0 == GL->adjList[i].in) stack[++top] = i;/* 將入度為0的頂點入棧 */ while (top != 0) { gettop = stack[top--]; cout << GL->adjList[gettop].data << " -> "; count++; /* 輸出i號頂點,並計數 */ for (pe = GL->adjList[gettop].firstedge; pe; pe = pe->next) { k = pe->adjvex; /* 將i號頂點的鄰接點的入度減1,如果減1后為0,則入棧 */ if (!--GL->adjList[k].in) stack[++top] = k; } } cout << endl; if (count < GL->numVertexes) return false; else return true; } int main(void) { MGraph MG; GraphAdjList GL; CreateMGraph(&MG); CreateALGraph(MG, &GL); if (TopologicalSort(GL)) cout << "It's a AOV network" << endl; else cout << "It's not a AOV network" << endl; return 0; } |
輸出為:
算法的代碼相比較最小生成樹和最短路徑是比較好理解的,注釋也比較清楚,這里就不費口舌了,如下圖7-8-4是將結點v3被刪除的模擬圖,其他依次
被刪除的結點情形類似,可類推。需要注意的是上面有個通過鄰接矩陣(事先確定)來生成鄰接表的函數CreateALGraph,因為是有向圖,所以針對一
條邊只插入一次EdgeNode, 且初始化in時注意是入度,即 (*GL)->adjList[j].in++; /* 注意這里是j */ 另外創建鄰接矩陣的函數CreateMGraph中因為是有
向圖,故矩陣並不是對稱的,需要注意。另外也不是網圖,故只用1表示弧存在,0表示弧不存在。
當然程序輸出的結果並不是唯一的一種拓撲排序方案。