数据结构之线性表——双链表的操作
双链表的操作核心是同时维护“前驱指针(prior
)”和“后继指针(next
)”,以保证双向链接的完整性。相比单链表,双链表的插入、删除因涉及双向指针修改,步骤更细致,但能更高效地支持反向操作。
1. 插入操作:在指定结点后插入新结点
双链表的插入需同时建立新结点与“前驱”“后继”的双向指针关系。如图2.10“双链表插入结点过程”所示,要在指针p
指向的结点(存储元素a
)之后插入新结点(存储元素x
),需分四步修改指针:① 新结点s
的prior
指向p
;② 新结点s
的next
指向p
的后继结点(图中存储c
的结点);③ p
的next
指向s
;④ p
原来的后继结点的prior
指向s
。这四步确保了新结点与前后结点的双向链接都被正确建立。
关键代码(假设双链表结点结构为DNode
,ElemType
为int
):
bool DListInsert(DNode *p, ElemType e) {if (p == NULL) return false; // p为空,无法插入DNode *s = (DNode *)malloc(sizeof(DNode));if (s == NULL) return false; // 内存分配失败s->data = e; // 新结点存储元素es->prior = p; // 步骤①:s的prior指向ps->next = p->next; // 步骤②:s的next指向p的后继if (p->next != NULL) { // 若p有后继,更新后继的priorp->next->prior = s; // 步骤④:p原后继的prior指向s}p->next = s; // 步骤③:p的next指向sreturn true;
}
代码逻辑:先为新结点s
分配内存并赋值,再依次建立s
与p
、p
后继的双向关系——s
的prior
连p
,s
的next
连p->next
;若p
原本有后继,需让该后继的prior
也连向s
;最后让p
的next
连向s
,完成插入。
2. 删除操作:移除指定结点
双链表的删除需同时断开待删结点与“前驱”“后继”的双向链接。假设要删除指针p
指向的结点(非头结点、非尾结点),步骤为:① 让p
的前驱结点的next
跳过p
,指向p
的后继;② 让p
的后继结点的prior
跳过p
,指向p
的前驱;③ 释放p
的内存,避免泄漏。
关键代码示例:
bool DListDelete(DNode *p) {if (p == NULL) return false; // p为空,无法删除if (p->prior != NULL) { // 若p有前驱,修改前驱的nextp->prior->next = p->next;}if (p->next != NULL) { // 若p有后继,修改后继的priorp->next->prior = p->prior;}free(p); // 释放待删结点的内存return true;
}
代码逻辑:先判断p
的前驱和后继是否存在,分别修改它们的指针(让前驱的next
连向后继,让后继的prior
连向前驱),从而“跳过”p
结点;最后释放p
的内存,完成删除。
双链表通过同时维护prior
和next
指针,保证了双向链接的完整性。虽然插入、删除的指针修改步骤比单链表多,但反向遍历或操作时无需额外遍历开销,在需要频繁双向操作的场景中更具优势。