[cpp]
typedef struct DNode {
struct DNode *prev;
struct DNode *next;
int data;
} DNode;
雙向鏈表每個節點包含前驅指針、後繼指針與數據域。本文實現帶頭節點的非循環雙向鏈表,頭節點不存儲有效數據,head->next 指向首元節點,head->prev = NULL。
[cpp]
DNode* init(int data) { // data 可作為頭節點標識或首元值
DNode *head = (DNode*)malloc(sizeof(DNode));
if (!head) exit(EXIT_FAILURE);
head->prev = NULL;
head->next = NULL;
head->data = data;
return head;
}
[cpp]
void insertLastNode(DNode *head, int data) {
DNode *newNode = (DNode*)malloc(sizeof(DNode));
newNode->data = data;
newNode->next = NULL;
// 找到尾節點
DNode *cur = head;
while (cur->next) cur = cur->next;
cur->next = newNode;
newNode->prev = cur;
}
[cpp]
// 找前驅節點
DNode* findPrev(DNode *head, int data) {
DNode *cur = head;
while (cur->next && cur->next->data != data)
cur = cur->next;
return cur; // 可能是尾節點(未找到)
}
void insertNode(DNode *head, int data) {
DNode *prev = findPrev(head, data);
if (!prev->next) return; // 未找到目標節點
DNode *newNode = (DNode*)malloc(sizeof(DNode));
newNode->data = data;
newNode->next = prev->next;
newNode->prev = prev;
prev->next->prev = newNode; // 關鍵:原後繼節點的 prev 指向新節點
prev->next = newNode;
}
[cpp]
DNode* findCur(DNode *head, int data) {
DNode *cur = head->next;
while (cur && cur->data != data) cur = cur->next;
return cur;
}
void deleteNode(DNode *head, int data) {
DNode *target = findCur(head, data);
if (!target) return; // 未找到
if (target->next) { // 非尾節點
target->next->prev = target->prev;
}
target->prev->next = target->next; // 統一處理:前驅的 next 跳過 target
free(target);
}

圖示:雙向鏈表插入/刪除時四個指針的重連順序,切記「先連外再連內」或「先斷後連」原則,避免斷鏈。
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
tmpNode->prev = NULL; tmpNode->prev->next = NULL; | 先斷開 prev 再訪問 prev->next 導致空指針解引用 | 先保存 prev = tmpNode->prev,再操作 |
未判斷 target->next 是否為 NULL | 刪除尾節點時 target->next->prev 觸發段錯誤 | 分情況處理或統一用 if (target->next) target->next->prev = target->prev; |
insertLastNode 直接 L->next = oNode | 未遍歷到尾部,導致丟失原鏈表 | 必須先找到尾節點 |
[cpp]
int main() {
DNode *list = init(0); // 頭節點 data=0 作為標識
insertLastNode(list, 10);
insertLastNode(list, 20);
insertLastNode(list, 30);
insertNode(list, 20); // 在 20 前插入 20(演示)
deleteNode(list, 20); // 刪除第一個 20
// 遍歷輸出
for (DNode *p = list->next; p; p = p->next)
printf("%d ", p->data);
// 輸出: 10 20 30
// 釋放鏈表
DNode *cur = list->next;
while (cur) {
DNode *tmp = cur;
cur = cur->next;
free(tmp);
}
free(list);
return 0;
}
head->prev = tail),統一邊界條件,代碼更簡潔。void* 存數據 + 銷毀回調函數)。