双向链表实现与算法分析
承接上文关于双向链表基础概念的内容,本节重点深入其具体实现细节,包括查找、插入、删除等核心操作,并对比顺序表与链表的差异,最后通过经典算法题巩固理解。
一、双向链表核心操作实现
1. 查找操作
双向链表的查找逻辑与单链表类似,但可以利用双向特性进行遍历优化。我们需要创建一个临时指针来遍历整个链表。
接口定义:
ListNode* LTFind(ListNode* h, type x);
实现逻辑: 首先判断链表是否为空,若为空直接返回 NULL。否则从首元节点开始遍历,直到找到目标数据或回到头结点。
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. 指定位置插入
插入操作分为'在指定节点之后'和'在指定节点之前'两种情况,核心在于维护好前驱和后继指针的指向关系。
在指定节点之后插入
该函数用于在节点 pos 之后插入新节点。关键在于先保存原后继节点,防止断链。
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 的前驱节点 p,然后利用上述'之后插入'的逻辑,或者手动调整四个指针。
void LTInsertfront(ListNode* h, ListNode* pos, type x) {
if (LTEmpty(h)) {
return ;
}
ListNode* p = h;
(p->next != h) {
(p->next == pos) {
;
}
p = p->next;
}
(p->next == h) {
;
}
ListNode* newnode = LTcreat(x);
ListNode* pr = p->next;
newnode->next = pr;
newnode->prev = p;
p->next = newnode;
pr->prev = newnode;
}


