承接前兩篇,本文完成 Prim 演算法的最後拼圖:無效邊清理、邊合法性判斷、鏈表排序三大核心輔助函數,並給出演算法工程化的四點心得。
delete_unvalid_line_from_list遍歷邊鏈表,呼叫 check_valid_for_line 判斷;若無效則從鏈表移除並釋放記憶體。注意指標操作需處理頭節點刪除與中間節點刪除兩種情況。
void delete_unvalid_line_from_list(DIR_LINE** ppHead, MINI_GENERATE_TREE* pMiniTree) {
DIR_LINE* prev = NULL;
DIR_LINE* pcur = *ppHead;
while (pcur) {
if (!check_valid_for_line(pcur, pMiniTree)) {
// 刪除當前節點
DIR_LINE* toDel = pcur;
if (prev == NULL) {
*ppHead = pcur->next;
pcur = *ppHead;
} else {
prev->next = pcur->next;
pcur = prev->next;
}
free(toDel);
continue;
}
prev = pcur;
pcur = pcur->next;
}
}
check_valid_for_line檢查邊的起點、終點是否「至少有一個」不在當前生成樹中。若兩者皆在樹中 → 會形成環 → 無效 (回傳 0);否則有效 (回傳 1)。
int check_valid_for_line(DIR_LINE* pDirLine, MINI_GENERATE_TREE* pMiniTree) {
int flag_start = 0, flag_end = 0;
for (int i = 0; i < pMiniTree->node_num; i++) {
if (pDirLine->start == pMiniTree->pNode[i]) flag_start = 1;
if (pDirLine->end == pMiniTree->pNode[i]) flag_end = 1;
}
return (flag_start && flag_end) ? 0 : 1;
}
sort_for_line_list + insert_for_sort_operation採用「線上插入排序」:將原鏈表拆解,逐節點插入新有序鏈表。適合動態產生邊的場景,避免陣列排序的額外空間。
void insert_for_sort_operation(DIR_LINE** ppNode, DIR_LINE* pNode) {
if (pNode->weight < (*ppNode)->weight) {
pNode->next = *ppNode;
*ppNode = pNode;
return;
}
DIR_LINE* cur = *ppNode;
DIR_LINE* prev = NULL;
while (cur) {
if (pNode->weight < cur->weight) break;
prev = cur;
cur = cur->next;
}
pNode->next = prev->next;
prev->next = pNode;
}
void sort_for_line_list(DIR_LINE** ppNode) {
if (!ppNode || !*ppNode) return;
DIR_LINE* curr = (*ppNode)->next;
(*ppNode)->next = NULL;
while (curr) {
DIR_LINE* prev = curr;
curr = curr->next;
insert_for_sort_operation(ppNode, prev);
}
}
Prim 演算法主流程:
MINI_GENERATE_TREE* get_mini_tree_from_graph(GRAPH* pGraph) {
MINI_GENERATE_TREE* pMiniTree = malloc(sizeof(MINI_GENERATE_TREE));
// 初始化:放入第一個節點
pMiniTree->node_num = 1;
pMiniTree->pNode[0] = pGraph->head->start;
while (1) {
DIR_LINE bestEdge;
get_dir_line_from_graph(pGraph, pMiniTree, &bestEdge);
if (bestEdge.start == 0) break; // 無有效邊 → 完成
insert_line_into_queue(&pMiniTree->pLine, bestEdge.start, bestEdge.end, bestEdge.weight);
insert_node_into_mini_tree(&bestEdge, pMiniTree);
}
return pMiniTree;
}
其中 get_dir_line_from_graph 內部串聯:收集候選邊 → 清理無效 → 排序 → 取最小 → 釋放暫存鏈表。