前言
上一篇文章讲解了双向链表的概念、结构及初始化、尾插、头插、尾删、头删等基础操作。本章将深入探讨双向链表的其余核心功能实现,对比顺序表与链表的区别,并通过经典算法题巩固理解。
一、实现双向链表
1. 查找操作
双向链表的查找逻辑与单链表类似,但可利用双向特性优化遍历方向。核心是创建一个临时指针进行遍历,直到找到目标或回到头结点。
ListNode* LTFind(ListNode* h, type x) {
if (LTEmpty(h)) { return NULL; }
ListNode* p = h->next;
while (p != h) {
if (p->data == x) { return p; }
p = p->next;
}
return NULL;
}
这里需要注意空链表的判断,以及循环终止条件 p != h,确保不会死循环。
2. 指定位置插入
在双向链表中插入分为'在指定节点后'和'在指定节点前'两种情况,关键在于指针的断开与重连顺序。
在指定节点之后插入
函数接口:void LTInsert(ListNode* pos, type x)
void LTInsert(ListNode* pos, type x) {
assert(pos);
ListNode* p = pos->next;
ListNode* newnode = LTcreat(x);
pos->next = newnode;
newnode->prev = pos;
newnode->next = p;
p->prev = newnode;
}
要点:必须先保存原后继节点 pos->next,否则插入后会丢失后续链表。调整顺序为:新节点指向前驱 -> 新节点指向后继 -> 前驱指向新节点 -> 后继指向前驱。
在指定节点之前插入
函数接口:void LTInsertfront(ListNode* h, ListNode* pos, type x)
void LTInsertfront(ListNode* h, ListNode* pos, type x) {
if (LTEmpty(h)) { return; }
ListNode* p = h;
// 先找到 pos 的前驱节点
while (p->next != h) {
if (p->next == pos) { break; }
p = p->next;
}
if (p->next == h) { return; } // 未找到
ListNode* newnode = LTcreat(x);
ListNode* pr = p->next;
newnode->next = pr;
newnode->prev = p;
p->next = newnode;
pr->prev = newnode;
}


