GESP5级C++考试语法知识(七、链表(二)双链表)

🌟《双链表王国大冒险》🌟

课程目标:

✅ 完全理解双链表结构

✅ 熟练写插入 / 删除

✅ 明白它比单链表强在哪里

✅ 能应对考试和竞赛题


🏰 第一章:为什么需要双链表?

🎯 故事:单行道 vs 双向马路

🚂 单链表(单行道)

复制代码
1 → 2 → 3 → 4

👉 只能往前走

👉 想回头?不行!


🚗 双链表(双向马路)

复制代码
1 ⇄ 2 ⇄ 3 ⇄ 4

👉 可以前进

👉 也可以后退!


🧠 第二章:双链表结构(看不同)

🎯 每个节点长这样:

复制代码
struct Node
{
    int data;
    Node* prev; // 指向前一个
    Node* next; // 指向后一个
};

🧩 图解

复制代码
NULL ← 1 ⇄ 2 ⇄ 3 → NULL

👉 每个节点有两个"指路牌"


🏗 第三章:创建双链表

🎯 手动创建

复制代码
Node* a = new Node{1, NULL, NULL};
Node* b = new Node{2, NULL, NULL};
Node* c = new Node{3, NULL, NULL};

// 连接
a->next = b;
b->prev = a;

b->next = c;
c->prev = b;

Node* head = a;

🔍 第四章:遍历(双向优势!)

🎯 正向遍历

复制代码
Node* p = head;

while(p != NULL)
{
    cout << p->data << " ";
    p = p->next;
}

🎯 反向遍历(双链表独有🔥)

复制代码
Node* p = tail;

while(p != NULL)
{
    cout << p->data << " ";
    p = p->prev;
}

👉 单链表做不到这一点!


➕ 第五章:插入操作


🎯 1️⃣ 头插法

复制代码
Node* insertHead(Node* head, int x)
{
    Node* node = new Node{x, NULL, head};

    if(head != NULL)
        head->prev = node;

    return node;
}

🎯 2️⃣ 在某节点后插入

👉 在 p 后插入 x

复制代码
void insertAfter(Node* p, int x)
{
    Node* node = new Node{x, p, p->next};

    if(p->next != NULL)
        p->next->prev = node;

    p->next = node;
}

🧠 插入口诀

复制代码
先连新节点 → 再改周围节点

❌ 第六章:删除操作(超级重点🔥🔥🔥)

🎯 删除某个节点 p

复制代码
void deleteNode(Node*& head, Node* p)
{
    if(p == NULL) return;

    // 如果是头节点
    if(p == head)
        head = p->next;

    if(p->prev != NULL)
        p->prev->next = p->next;

    if(p->next != NULL)
        p->next->prev = p->prev;

    delete p;
}

🧠 删除口诀(必背)

复制代码
断前 → 断后 → 释放

🔄 第七章:双链表反转(比单链表更简单!)

🎯 思路

👉 把 prev 和 next 交换!


💻

复制代码
Node* reverse(Node* head)
{
    Node* p = head;
    Node* newHead = NULL;

    while(p)
    {
        // 交换指针
        Node* tmp = p->next;
        p->next = p->prev;
        p->prev = tmp;

        newHead = p;
        p = tmp;
    }

    return newHead;
}

🧱 第八章:带头结点(哨兵节点)技巧🔥

🎯 为什么用?

👉 避免特殊情况(空链表 / 头节点)


🎯 结构

复制代码
HEAD ⇄ 1 ⇄ 2 ⇄ 3

💡 插入更简单:

复制代码
void insert(Node* head, int x)
{
    Node* node = new Node{x, head, head->next};

    if(head->next)
        head->next->prev = node;

    head->next = node;
}

⚔️ 第九章:双链表 vs 单链表

对比 单链表 双链表
指针数 1个 2个
能否回头
删除操作 麻烦 简单🔥
内存占用
使用场景 普通 LRU缓存🔥

🎯 第十章:经典应用------LRU缓存(了解)

👉 浏览器"最近访问记录"

👉 最近用的放前面

👉 很久没用的删掉

👉 双链表 + 哈希表 = 高级结构🔥

结构 查找效率 插入/删除效率 顺序维护
纯哈希表 O(1) O(1) ❌ 无序
纯双链表 O(n) O(1)(已知位置) ✅ 有序
哈希表 + 双链表 O(1) O(1) ✅ 有序

🧠 核心总结(非常重要)

🎯 双链表三大操作本质

复制代码
插入 = 改4条指针
删除 = 改2~4条指针
反转 = 交换prev和next

🎁 最强口诀(考试必背🔥)

复制代码
双链表有两指针,前驱后继都能寻
插入节点四步走,新前后要连好
删除节点要小心,前后断开再释放
反转链表很简单,prev next互交换

🏁 学完我们应该能做到:

✅ 手写双链表结构

✅ 正向 + 反向遍历

✅ 任意位置插入

✅ 删除任意节点

✅ 反转链表

✅ 理解为什么它比单链表强


🌟《双链表5道经典训练题(含LRU简化版)》🌟

👉 目标:从"会写"到"会用"再到"会做竞赛题"


🧩 第1题:正向 + 反向遍历(基础)

1、🎯 故事

你是列车管理员,不仅要从前往后检查,还要倒着检查!


2、🚂 图解

复制代码
NULL ← 1 ⇄ 2 ⇄ 3 → NULL

3、💡 思路

✔ 正向:next

✔ 反向:prev


4、💻 代码

复制代码
void printForward(Node* head)
{
    Node* p = head;
    while(p)
    {
        cout << p->data << " ";
        p = p->next;
    }
}

void printBackward(Node* tail)
{
    Node* p = tail;
    while(p)
    {
        cout << p->data << " ";
        p = p->prev;
    }
}

🧩 第2题:在指定位置插入(进阶)

1、🎯 故事

新乘客要插到第 k 个位置 🚃


2、💡 思路

👉 找第 k 个节点 p

👉 插入在 p 前面


3、💻代码

复制代码
Node* insertAt(Node* head, int k, int x)
{
    Node* node = new Node{x, NULL, NULL};

    if(k == 1)
    {
        node->next = head;
        if(head) head->prev = node;
        return node;
    }

    Node* p = head;
    for(int i = 1; i < k-1 && p; i++)
        p = p->next;

    if(p == NULL) return head;

    node->next = p->next;
    node->prev = p;

    if(p->next)
        p->next->prev = node;

    p->next = node;

    return head;
}

🧩 第3题:删除第k个节点

1、🎯 故事

第k个乘客要下车 🚫


2、💡 思路

👉 找到第k个节点

👉 修改前后指针


3、💻代码

复制代码
Node* deleteK(Node* head, int k)
{
    if(head == NULL) return head;

    Node* p = head;

    for(int i = 1; i < k && p; i++)
        p = p->next;

    if(p == NULL) return head;

    if(p == head)
        head = p->next;

    if(p->prev)
        p->prev->next = p->next;

    if(p->next)
        p->next->prev = p->prev;

    delete p;
    return head;
}

🧩 第4题:判断是否回文链表(经典🔥)

1、🎯 故事

火车从前看和从后看是不是一样?

复制代码
1 2 3 2 1 ✅
1 2 3 ❌

2、💡 思路

👉 双指针:

  • 左指针:head

  • 右指针:tail


3、💻代码

复制代码
bool isPalindrome(Node* head)
{
    if(head == NULL) return true;

    Node* tail = head;
    while(tail->next)
        tail = tail->next;

    Node* l = head;
    Node* r = tail;

    while(l != r && l->prev != r)
    {
        if(l->data != r->data)
            return false;

        l = l->next;
        r = r->prev;
    }

    return true;
}

🧩 第5题:LRU缓存(进阶了解🔥🔥🔥)

1、🎯 故事

👉 浏览器最近访问记录:

复制代码
访问:1 → 2 → 3 → 4

👉 再访问 2:

复制代码
变成:2 → 4 → 3 → 1

👉 最常用在前面!


2、🧠 核心思想

👉 双链表负责"顺序"

👉 最新的放前面

👉 删除尾巴(最久没用)


3💡 简化规则(不使用map)

✔ 访问:

  • 如果存在 → 移到头

  • 如果不存在 → 插入头

  • 超容量 → 删除尾


4、💻 核心代码

复制代码
struct Node
{
    int data;
    Node* prev;
    Node* next;
};

class LRU
{
public:
    Node* head;
    Node* tail;
    int cap;
    int size;

    LRU(int c)
    {
        head = tail = NULL;
        cap = c;
        size = 0;
    }

    // 删除节点
    void remove(Node* p)
    {
        if(p == head) head = p->next;
        if(p == tail) tail = p->prev;

        if(p->prev) p->prev->next = p->next;
        if(p->next) p->next->prev = p->prev;
    }

    // 插到头
    void insertHead(Node* p)
    {
        p->prev = NULL;
        p->next = head;

        if(head) head->prev = p;
        head = p;

        if(tail == NULL) tail = p;
    }

    // 访问
    void access(int x)
    {
        Node* p = head;

        // 查找
        while(p && p->data != x)
            p = p->next;

        if(p) // 存在
        {
            remove(p);
            insertHead(p);
        }
        else
        {
            Node* node = new Node{x, NULL, NULL};
            insertHead(node);
            size++;

            if(size > cap)
            {
                Node* t = tail;
                remove(t);
                delete t;
                size--;
            }
        }
    }
};

🧠 总结:双链表考题特点

🎯 答题技巧

复制代码
1️⃣ 插入 = 改4条指针
2️⃣ 删除 = 改前后指针
3️⃣ 多用 head / tail
4️⃣ 回文 = 双指针
5️⃣ LRU = 双链表核心应用🔥(了解)

🎁 终极口诀(背这个就稳了🔥)

复制代码
双链表,双方向,前驱后继都要连
插入节点四步走,前后关系别搞乱
删除节点要小心,前后断开再释放

相关推荐
旖-旎2 小时前
二分查找(寻找旋转排序数组中的最小值)(7)
c++·算法·二分查找·力扣
C羊驼2 小时前
C/C++数据结构与算法:穷举法
c语言·c++·笔记·学习·算法
十五年专注C++开发2 小时前
libuv:一个跨平台的C++异步 I/O 库
开发语言·c++·node.js·libuv·vlibuv
qq_417695052 小时前
C++中的解释器模式
开发语言·c++·算法
xiaoye-duck3 小时前
《算法题讲解指南:动态规划算法--路径问题》--9.最小路径和,10.地下城游戏
c++·算法·动态规划
刺客xs3 小时前
c++模板
java·开发语言·c++
2301_818419013 小时前
C++中的状态模式实战
开发语言·c++·算法
仰泳的熊猫3 小时前
题目2576:蓝桥杯2020年第十一届省赛真题-解码
数据结构·c++·算法·蓝桥杯
CSDN_kada3 小时前
杭电网安复试编程Day23
c++·考研·算法