🔀

链表的种类

单行道、双向街道和地铁环线

💡 打个比方

单链表是单行道:每个路口只写着一个"下一站地址",只能往前走,想回头只能从起点重走。

双链表是双向街道:每个节点多存一个"上一站地址"(prev),既能往前也能往后,想去哪边都行。

循环链表是地铁环线:最后一站的"下一站"又指回第一站,永远走不到尽头,一圈一圈转。

三种链表:单链表(val + next,只能前进);双链表(val + prev + next,双向通行);循环链表(尾节点的 next 指回头部,像地铁环线)。删除节点永远只改邻居的"地址"。

🖼️ 大图看明白

单链表 双链表 循环链表 ① 双链表:每个节点存两个地址,上一站 prev、下一站 next 10 prev | next 20 prev | next 30 prev | next head tail ② 循环链表:尾节点的 next 指回头部,像地铁环线 A B C C 的 next 指回 A(tail->next = head) ③ 删除对比:单链表改 1 个地址,双链表要改邻居的 2 个地址 单链表:删 20 10 20 30 只改 10 的 next,让它直连 30 双链表:删 20 10 20 30 改 10 的 next 和 30 的 prev,都绕开 20

📏 建双链表,只要 3 步

  1. 第 1 步:造节点。新建一个节点,把值存进 val,prev 和 next 先都设为 NULL(还没有邻居)。
  2. 第 2 步:尾插接上。把新节点的 prev 指向旧 tail,旧 tail 的 next 指向新节点;然后让 tail 变成新节点。如果这是第一个节点,head 也指向它。
  3. 第 3 步:双向遍历。从 head 顺着 next 往前走是正序;从 tail 顺着 prev 往后走是倒序。两个方向都能走到所有节点。
为什么双链表删除更"麻烦"?单链表删节点只需改它前一个的 next;双链表要改两个邻居:前一个的 next 和后一个的 prev,都绕过被删节点。但好处是:拿着任意一个节点,不用从 head 重走,就能直接找它的上一个。

💻 C++ 怎么写

// 双链表节点:值 + 上一站 + 下一站
struct Node {
    int val;
    Node *prev;
    Node *next;
};
Node *head = nullptr, *tail = nullptr;

void tailInsert(int x) {        // 尾插
    Node *p = new Node{x, tail, nullptr};
    if (tail) tail->next = p;   // 旧尾指向新节点
    tail = p;                   // 新节点成为新尾
    if (!head) head = p;        // 第一个节点也是头
}

int main() {
    tailInsert(10); tailInsert(20); tailInsert(30);
    Node *p = head;
    while (p) {                 // 正序遍历
        cout << p->val << " "; p = p->next;
    }
    p = tail;
    while (p) {                 // 倒序遍历
        cout << p->val << " "; p = p->prev;
    }
    return 0;
}
考点提醒:循环链表判断"走完了"不能靠 next == NULL,要靠"又走回 head"(next == head)。双链表节点比单链表多一个指针,多占空间,换来的是双向通行。

✋ 你来试试

题 1:双链表 10↔20↔30↔40,删掉 20,需要改哪两个地址?
改两处:① 20 的前邻居 10:10->next = 20->next(让 10 直连 30);② 20 的后邻居 30:30->prev = 20->prev(让 30 回连 10)。最后释放 20 这个节点。单链表只需要做第 ① 步。
题 2:为什么循环链表不能像普通链表那样"走到 NULL 就停"?
因为循环链表的尾节点 next 指回 head,顺着走永远走不到 NULL,会无限循环!判断"走完整圈"的办法是:记录起点 head,当再次遇到 head(p->next == head)时说明一圈结束。这也是循环链表求尾节点的技巧:从 head 一直走到 next == head 的那个节点,它就是 tail。
🎯 一句话记住:单链表=单行道(只有 next);双链表=双向街道(prev+next,删除要改两个邻居);循环链表=地铁环线(尾接回头部,判断终止靠"走回 head")。