VOL.2 拓撲排序與關鍵路徑
拓撲排序與關鍵路徑
對於一個給定的工程施工圖,對該圖以邊為單位從鍵盤輸入,實現拓撲排序演算法和關鍵路徑演算法,找出並輸出該圖的關鍵路徑。
要求
圖的儲存結構採用鄰接表儲存;
圖的頂點和邊通過鍵盤輸入;
在螢幕上輸出建立的鄰接表,每行輸出一個頂點的單連結串列,例如:E: ->G->H
程式執行介面友好,輸入要有合理的提示(輸入資料的內容以及格式要求)。
測試資料:工程施工圖如下:
1.圖的儲存採用鄰接表方式,頂點節點包含四個域:頂點序號域、最早開始時間域、最遲開始時間域、鄰接邊節點域,邊節點包含三個域:頂點序號域、邊節點域、權值域。
typedef struct edgeNode {
int order;
int weight;
edgeNode* next_edge;
}edgeNode;
typedef struct vertexNode {
int order;
int early_time;
int late_time;
edgeNode* next_edge;
}vertexNode;
vertexNode* initialize_vertexNode() {
// 頂點節點初始化
vertexNode* vertex_node = (vertexNode*)malloc(sizeof(vertexNode));
vertex_node->order = INF;
vertex_node->early_time = 0;
vertex_node->late_time = INF;
vertex_node->next_edge = NULL;
return vertex_node;
}
edgeNode* initialize_edgeNode() {
// 邊節點初始化
edgeNode* edge_node = (edgeNode*)malloc(sizeof(edgeNode));
edge_node->weight = INF;
edge_node->order = INF;
edge_node->next_edge = NULL;
return edge_node;
}
2.圖的建立要求使用鍵盤輸入,首先由使用者輸入頂點數量,生成一個n*n的一維動態陣列,初始化所有值為INF,a[i * n + j] = x 表示從點i到點j存在邊,權值為x。掃描陣列建立鄰接表,其中INF表示不存在邊,不予加入鄰接表。
int* initialize_adj_matrix(int vertex_n, int edge_n) {
// 鄰接矩陣建立
printf("\n");
int* adj_matrix = (int*)malloc(sizeof(int) * vertex_n * vertex_n);
for (int i = 0; i < vertex_n * vertex_n; i++) {
adj_matrix[i] = INF;
}
getchar();
int a, b, c;
for (int i = 1; i <= edge_n; i++) {
printf("請輸入第%d條邊的兩個端點(按照相應的順序)與權值:", i);
scanf("%d%d%d", &a, &b, &c);
adj_matrix[a * vertex_n + b] = c;
}
return adj_matrix;
}
vertexNode** initialize_vertexs(int* adj_matrix, int lenth) {
// 鄰接矩陣轉鄰接表
vertexNode** vertexs = (vertexNode**)malloc(sizeof(vertexNode) * lenth);
for (int i = 0; i < lenth; i++) {
edgeNode* edge_node = initialize_edgeNode();
vertexs[i] = initialize_vertexNode();
vertexs[i]->order = i;
vertexs[i]->next_edge = edge_node;
for (int j = 0; j < lenth; j++) {
if (adj_matrix[i * lenth + j] < INF) {
edge_node->order = j;
edge_node->weight = adj_matrix[i * lenth + j];
edgeNode* edge_next_node = initialize_edgeNode();
edge_node->next_edge = edge_next_node;
edge_node = edge_node->next_edge;
}
}
}
return vertexs;
}
void print_adj_list(vertexNode** vertexs, int lenth) {
// 輸出鄰接表
printf("\n鄰接表如下:\n");
for (int i = 0; i < lenth; i++) {
printf("%d: ", vertexs[i]->order);
edgeNode* edge_node = vertexs[i]->next_edge;
while (edge_node->order != INF) {
printf("->%d", edge_node->order);
edge_node = edge_node->next_edge;
}
printf("\n");
}
}
3.拓撲排序,遍歷一次鄰接表用一個陣列記錄每個頂點的前驅節點個數,其中某個頂點被加入拓撲排序序列後,其後繼節點的前驅結點個數-1,繼續訪問前驅結點個數為0的點。用一維陣列記錄拓撲排序序列。
int* topological_sort(vertexNode** vertexs, int lenth) {
int* sort_arr = (int*)malloc(sizeof(int) * lenth);
int* count_arr = (int*)malloc(sizeof(int) * lenth);
for (int i = 0; i < lenth; i++) {
sort_arr[i] = 0;
count_arr[i] = 0;
}
for (int i = 0; i < lenth; i++) {
edgeNode* edge_node = vertexs[i]->next_edge;
while (edge_node->order != INF) {
count_arr[edge_node->order]++;
edge_node = edge_node->next_edge;
}
}
for (int i = 0; i < lenth; i++) {
int order;
for (int j = 0; j < lenth; j++) {
if (count_arr[j] == 0) {
order = j;
break;
}
}
sort_arr[i] = order;
count_arr[order] = -1;
edgeNode* edge_node = vertexs[order]->next_edge;
while (edge_node->order != INF) {
count_arr[edge_node->order]--;
edge_node = edge_node->next_edge;
}
}
printf("\n拓撲排序順序為:");
for (int i = 0; i < lenth; i++) {
printf("%d", sort_arr[i]);
}
printf("\n");
return sort_arr;
}
4.完成拓撲排序後,按照拓撲排序的順序依次訪問頂點節點,修改頂點的early_time域,每個節點的early_time域在初始化時置為0。對每條邊進行操作時,當前邊的起點節點early_time、邊權值的和與當前邊的終點節點的early_time進行比較,若前者大,則進行修改,遍歷所有的邊後完成每個節點early_time的處理。再按照拓撲排序的逆序列依次訪問頂點節點,修改頂點節點的late_time域,每個節點的late_time域在初始化時置為INF。其中,匯點的late_time等於early_time直接進行修改不需要進行比較,也不需要進行訪問。對每條邊進行操作時,當前邊的起點節點late_time域與當前邊的終點節點late_time、邊權值的差進行比較,若後者小,則進行修改,遍歷所有邊後完成每個節點late_time的處理。修改完後,通過頂點節點陣列按照邊的順序遍歷並列印所有early_time與late_time相等的頂點即關鍵活動,繼而求得關鍵路徑。
void get_critical_paths(int* sort_arr, vertexNode** vertexs, int lenth, int start_order, int end_order) {
edgeNode* edge_node;
for (int i = 0; i < lenth; i++) {
edge_node = vertexs[i]->next_edge;
while (edge_node->order != INF) {
if (vertexs[i]->early_time + edge_node->weight > vertexs[edge_node->order]->early_time)
vertexs[edge_node->order]->early_time = vertexs[i]->early_time + edge_node->weight;
edge_node = edge_node->next_edge;
}
}
vertexs[lenth - 1]->late_time = vertexs[lenth - 1]->early_time;
for (int i = lenth - 2; i >= 0; i--) {
edge_node = vertexs[i]->next_edge;
while (edge_node->order != INF) {
if (vertexs[i]->late_time > vertexs[edge_node->order]->late_time - edge_node->weight)
vertexs[i]->late_time = vertexs[edge_node->order]->late_time - edge_node->weight;
edge_node = edge_node->next_edge;
}
}
int* critical_paths = (int*)malloc(sizeof(int) * lenth);
critical_paths[0] = start_order;
int order = 1;
while (1) {
if (critical_paths[order - 1] == end_order) break;
edge_node = vertexs[critical_paths[order - 1]]->next_edge;
while (1) {
if (vertexs[edge_node->order]->early_time == vertexs[edge_node->order]->late_time) {
critical_paths[order] = edge_node->order;
order++;
break;
}
else edge_node = edge_node->next_edge;
}
}
printf("\n關鍵路徑為:");
printf("%d", critical_paths[0]);
for (int i = 1; i < order; i++) {
printf("->%d", critical_paths[i]);
}
}
最終效果:
問題與總結:吸取了上一次的教訓,把每個用到的結構體節點以及陣列仔細進行了初始化,在這次程式碼編寫過程中,沒有出現任何記憶體訪問衝突的問題!並且解決了上一次沒有解決的鄰接表頂點節點用陣列儲存的問題,大大簡化了程式碼複雜程度也提高了可讀性!
相關文章
- 拓撲排序詳解(梅開二度之dfs版按字典序輸出拓撲路徑+dfs版輸出全部拓撲路徑排序
- AOV網與拓撲排序排序
- 拓撲排序排序
- 拓撲排序,YYDS排序
- 圖論——拓撲排序圖論排序
- 筆記:拓撲排序筆記排序
- 拓撲排序小結排序
- Reward (圖論+拓撲排序)圖論排序
- 【筆記/模板】拓撲排序筆記排序
- DFS實現拓撲排序排序
- 圖的拓撲排序詳解與實現排序
- 拓撲排序就這麼回事排序
- 演算法-圖論-拓撲排序演算法圖論排序
- 有向圖的拓撲排序——DFS排序
- 網路拓撲結構
- Day2 尤拉路,拓撲排序和差分約束排序
- (set+拓撲排序) CF1572A Book排序
- 圖解拓撲排序+程式碼實現圖解排序
- 網路拓撲圖:網路拓撲圖介紹及線上製作
- 拓撲排序 (BFS )DAG (有向無環圖)排序
- 關鍵路徑JS實現JS
- 優化關鍵渲染路徑優化
- Istio全景監控與拓撲
- 【Tarjan 拓撲排序 dp】P3387 【模板】縮點排序
- 網路拓撲—FTP服務搭建FTP
- 商企網路拓撲的搭建
- Noc拓撲
- 洛谷P3953 逛公園(dp 拓撲排序)排序
- 牛客 51011 可達性統計(拓撲排序,bitset)排序
- 【BZOJ-1565】植物大戰殭屍 拓撲排序 + 最小割排序
- MySQL 8 複製(六)——拓撲與效能MySql
- 網路拓撲—WEB-IIS服務搭建Web
- 企業網路拓撲RSTP功能例項
- DDD與團隊拓撲以及微服務之間的關係圖 - aleixmorgadas微服務
- 企業網路拓撲MSTP功能例項(二)
- 網路拓撲例項之RRPP單環(五)
- Leetcode 1691. 堆疊長方體的最大高度(拓撲排序 + DP)LeetCode排序
- 網路基本認知(2)--網路拓撲圖的規劃與設計