Skip to content

双链表

相比单链表的改进

单链表只能往后走,想找前驱结点必须从头遍历。双链表用一个 prior 指针的代价,换来了 O(1) 访问前驱——删除已知结点从 O(n) 降到 O(1)。

408 考研中,插入和删除操作的指针修改顺序是选择题和代码题的高频考点。

核心思想

双链表的核心特点:

  • 双向链接:每个结点包含 prior(前驱指针)、data(数据域)、next(后继指针)三个部分
  • 双向遍历:既可以从前往后,也可以从后往前遍历
  • O(1) 访问前驱:已知某结点时,可以直接通过 prior 指针找到其前驱,无需从头遍历

结点结构示意:

┌───────┬──────┬───────┐
│ prior │ data │ next  │
└───────┴──────┴───────┘

结点定义(C 语言):

c
typedef struct DNode {
    ElemType data;
    struct DNode *prior, *next;
} DNode, *DLinklist;

与单链表类似,双链表通常也设置头结点,头结点的 priorNULL,尾结点的 nextNULL

双链表插入操作:4 步指针修改

在结点 p 之后插入新结点 s,必须按正确顺序修改 4 个指针,避免断链:

关键:第 1、2 步必须在第 4 步之前执行,否则 p->next 被覆盖,原后继结点地址丢失。

易错:双链表插入/删除时指针操作的顺序很重要。插入时一般先修改新结点的两个指针,再修改前后结点的指针。如果顺序搞错,可能导致链断裂丢失结点。408 算法题经常要求写出正确的指针操作顺序。

交互可视化

通过下方的交互动画,你可以逐步观察双链表的执行过程:

加载可视化中...

操作详解

插入操作

在结点 p 之后插入新结点 s,需要修改四个指针。注意修改顺序,避免断链。

关键步骤:

  1. s->next = p->next(新结点指向 p 的后继)
  2. p->next != NULL,则 p->next->prior = s(原后继的前驱指向新结点)
  3. s->prior = p(新结点的前驱指向 p)
  4. p->next = s(p 的后继指向新结点)

要点:步骤 1、2 必须在步骤 4 之前完成,否则 p->next 会被覆盖,导致原后继结点丢失。

c
// 在 p 结点之后插入结点 s
bool InsertNextDNode(DNode *p, DNode *s) {
    if (p == NULL || s == NULL) return false;
    s->next = p->next;
    if (p->next != NULL)
        p->next->prior = s;
    s->prior = p;
    p->next = s;
    return true;
}

删除操作

删除结点 p 的后继结点 q,需要修改两个指针,然后释放 q 的空间。

关键步骤:

  1. p->next = q->next(p 的后继跳过 q,指向 q 的后继)
  2. q->next != NULL,则 q->next->prior = p(q 的后继的前驱指向 p)
  3. free(q)(释放被删除结点的空间)
c
// 删除 p 结点的后继结点
bool DeleteNextDNode(DNode *p) {
    if (p == NULL || p->next == NULL) return false;
    DNode *q = p->next;
    p->next = q->next;
    if (q->next != NULL)
        q->next->prior = p;
    free(q);
    return true;
}

与单链表对比:单链表删除结点 p 的后继时只需修改一个指针(p->next),而双链表需要额外修改后继结点的 prior 指针。但双链表的优势在于——已知待删除结点本身时,可以 O(1) 完成删除,无需像单链表那样先找前驱。

复杂度分析

操作时间复杂度说明
按位查找O(n)需从头结点依次遍历
按值查找O(n)最坏需遍历整个表
已知结点后插入O(1)直接修改前后指针
已知结点后删除其后继O(1)直接修改前后指针
已知结点删除自身O(1)可直接访问前驱,无需遍历(单链表需 O(n))
在给定位置插入(按位序)O(n)需先遍历找到目标位置

空间复杂度:每个结点比单链表多一个 prior 指针,存储开销更大,属于典型的以空间换时间策略。

考研高频考点

  • ⭐ 插入操作的指针修改顺序(选择题/代码题高频,顺序错误会导致断链)
  • ⭐ 删除操作中对边界情况的处理(被删除结点是尾结点时 q->next == NULL
  • ⭐ 双链表 vs 单链表的优缺点对比(简答题常考)
    • 优点:支持双向遍历、已知结点可 O(1) 删除自身、查找前驱 O(1)
    • 缺点:每个结点多一个指针域,存储开销更大;插入/删除时需维护更多指针
  • ⭐ 已知结点删除自身的时间复杂度:双链表 O(1),单链表 O(n)
  • 双链表结点结构的存储密度(低于单链表,远低于顺序表)

相关知识

  • 双链表是单链表的增强版本,用额外一个指针域换取 O(1) 访问前驱的能力
  • 循环链表中的循环双链表将双链表首尾相连,插入删除时无需特判尾结点
  • 树结点的 lchild/rchild 本质就是双链表的变体——线索二叉树更是直接利用空指针域存放前驱/后继信息

真题练习

相关真题(3题)

2026Q2选择题2分

双向链表指针操作:遍历链表修改 p2 指针需处理尾结点边界

2023Q2选择题2分

双向链表插入:在指定位置后插入结点需要补充的指针赋值语句

2016Q2选择题2分

双向循环链表:删除指针所指结点的正确语句序列