GESP5级C++考试语法知识(八、链表(三)循环链表)


🌟《循环链表王国大冒险》🌟

学习目标:

✅ 真正理解循环链表

✅ 会写代码

✅ 会做题

✅ 不再怕"死循环"😄


🏰 第一章:什么是循环链表?

1、🎯 故事:神奇的"永动火车"

(1)普通链表是这样的:

复制代码
1 → 2 → 3 → NULL

🚫 到头就结束了


(2)但循环链表是:

复制代码
1 → 2 → 3
↑       ↓
└───────┘

👉 最后一个节点 指回第一个!


2、🧠 核心一句话

👉 循环链表 = 没有 NULL,永远走不完!


⚠️ 第二章:为什么循环链表危险?

1、🎯 故事

你在火车上一直走:

复制代码
1 → 2 → 3 → 1 → 2 → 3 → 1 → ...

😱 永远停不下来!


2、🚨 错误写法(会死循环)

复制代码
while(p != NULL)   // ❌ 永远不会为 NULL
{
    cout << p->data;
    p = p->next;
}

3、✅ 正确遍历方法(重点!!!)

👉 方法1:回到起点停止

复制代码
Node* p = head;

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

🏗 第三章:如何创建循环链表?

🎯 方法1:手动连接

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

a->next = b;
b->next = c;
c->next = a;  // 🔥 关键:指回头

Node* head = a;

🎯 方法2:尾插法(推荐🔥)

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

    if(tail == NULL)
    {
        node->next = node; // 自己指自己
        return node;
    }

    node->next = tail->next;
    tail->next = node;

    return node; // 新节点成为尾
}

🧠 关键理解

👉 循环链表常用:

复制代码
tail->next = head

所以:

👉 只存 tail 就够了!


🔍 第四章:循环链表遍历(重点)

🎯 标准模板(必须会!)

复制代码
void print(Node* tail)
{
    if(tail == NULL) return;

    Node* p = tail->next; // head

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

➕ 第五章:插入操作


🎯 1️⃣ 头部插入

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

    if(tail == NULL)
    {
        node->next = node;
        return node;
    }

    node->next = tail->next;
    tail->next = node;

    return tail;
}

🎯 2️⃣ 尾部插入(最常用🔥)

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

    if(tail == NULL)
    {
        node->next = node;
        return node;
    }

    node->next = tail->next;
    tail->next = node;

    return node; // 更新尾
}

❌ 第六章:删除节点(重点难点🔥)

🎯 删除值为 x 的节点

复制代码
Node* deleteValue(Node* tail, int x)
{
    if(tail == NULL) return NULL;

    Node* cur = tail->next; // head
    Node* prev = tail;

    do
    {
        if(cur->data == x)
        {
            // 只有一个节点
            if(cur == prev)
            {
                delete cur;
                return NULL;
            }

            prev->next = cur->next;

            // 删除的是尾
            if(cur == tail)
                tail = prev;

            delete cur;
            return tail;
        }

        prev = cur;
        cur = cur->next;

    }while(cur != tail->next);

    return tail;
}

🔄 第七章:循环链表的"反转"难点

👉 ⚠️ 重要:

循环链表一般 不直接反转!


🎯 方法:先变普通链表,再反转

复制代码
// 1️⃣ 断开
Node* head = tail->next;
tail->next = NULL;

// 2️⃣ 普通反转(你已经会了)
head = reverse(head);

// 3️⃣ 找新尾
Node* p = head;
while(p->next)
    p = p->next;

// 4️⃣ 再连成环
p->next = head;
tail = p;

🧠 第八章:循环链表 vs 单链表(核心区别)

特点 单链表 循环链表
结尾 NULL 指回头
遍历 while do-while
风险 安全 死循环⚠️
常用 普通题 约瑟夫问题🔥

🎯 第九章:经典应用------约瑟夫问题(必考🔥🔥🔥)

1、🎯 故事

(1)一群小朋友围成一圈:

复制代码
1 2 3 4 5

(2)每数到3的人淘汰!


(3)👉 用循环链表完美解决!


2、💻 核心代码

复制代码
Node* solve(int n, int k)
{
    Node* tail = NULL;

    // 建环
    for(int i = 1; i <= n; i++)
        tail = insertTail(tail, i);

    Node* cur = tail->next;
    Node* prev = tail;

    while(cur != cur->next)
    {
        for(int i = 1; i < k; i++)
        {
            prev = cur;
            cur = cur->next;
        }

        prev->next = cur->next;
        delete cur;
        cur = prev->next;
    }

    return cur; // 最后一个
}

第九章:🎁 最终口诀(超级重要!)

复制代码
循环链表要记牢,没有NULL很奇妙
遍历必须do-while,否则程序停不掉
插入删除改指针,tail位置要分清
考试约瑟夫问题常出现,掌握它就轻松过

🏁 学完我们要做到:

✅ 会画循环链表

✅ 会安全遍历(不会死循环)

✅ 会插入 / 删除

✅ 理解 tail 的作用

✅ 会做约瑟夫问题


附:🌟《循环链表5道专项训练(从简单到竞赛)》🌟


🧩 第1题:安全遍历循环链表(基础)

1、🎯 故事

你在"无限火车"上巡逻,不能一直绕圈!


2、🚂 图解

复制代码
1 → 2 → 3
↑       ↓
└───────┘

3、💡 思路

👉 关键:

✔ 从 head 出发

✔ 回到 head 停止


4、💻 核心代码

复制代码
void print(Node* tail)
{
    if(tail == NULL) return;

    Node* p = tail->next; // head

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

🧩 第2题:统计节点个数(进阶一点)

1、🎯 故事

你要数一数"圆形火车"有多少节🚃


2、💡 思路

👉 do-while + 计数器


3、💻核心代码

复制代码
int count(Node* tail)
{
    if(tail == NULL) return 0;

    int cnt = 0;
    Node* p = tail->next;

    do
    {
        cnt++;
        p = p->next;
    }while(p != tail->next);

    return cnt;
}

🧩 第3题:删除所有值为x的节点(重点🔥)

1、🎯 故事

火车上所有"调皮的乘客x"都要下车!


2、⚠️ 难点

👉 可能删除:

  • 头节点

  • 尾节点

  • 所有节点(变空)


3、💡 思路

👉 用 prev + cur

👉 循环一圈

👉 特别注意 tail 更新!


4、💻核心代码:

复制代码
Node* deleteAll(Node* tail, int x)
{
    if(tail == NULL) return NULL;

    Node* cur = tail->next;
    Node* prev = tail;

    do
    {
        if(cur->data == x)
        {
            // 只有一个节点
            if(cur == prev)
            {
                delete cur;
                return NULL;
            }

            prev->next = cur->next;

            if(cur == tail)
                tail = prev;

            Node* tmp = cur;
            cur = cur->next;
            delete tmp;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }

    }while(cur != tail->next);

    return tail;
}

🧩 第4题:循环链表右移k步(技巧🔥)

1、🎯 故事

整个火车向右旋转k步!


2、🚂 示例

复制代码
原来:1 2 3 4
右移2步 → 3 4 1 2

3、💡 思路

👉 本质:

✔ 找新尾巴

✔ tail往前走


4、💻核心代码

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

    int n = 1;
    Node* p = tail->next;

    while(p != tail)
    {
        n++;
        p = p->next;
    }

    k %= n;

    for(int i = 0; i < k; i++)
        tail = tail->next;

    return tail;
}

🧩 第5题:约瑟夫问题(竞赛经典🔥🔥🔥)

1、🎯 故事

小朋友围成一圈:

复制代码
1 2 3 4 5

每数到 m 的人淘汰!


2、💡 思路

👉 循环链表 + 删除

👉 每次走 m-1 步


3、💻核心代码

复制代码
Node* josephus(int n, int m)
{
    Node* tail = NULL;

    // 建环
    for(int i = 1; i <= n; i++)
    {
        Node* node = new Node{i, NULL};

        if(tail == NULL)
        {
            node->next = node;
            tail = node;
        }
        else
        {
            node->next = tail->next;
            tail->next = node;
            tail = node;
        }
    }

    Node* cur = tail->next;
    Node* prev = tail;

    while(cur != cur->next)
    {
        for(int i = 1; i < m; i++)
        {
            prev = cur;
            cur = cur->next;
        }

        prev->next = cur->next;
        delete cur;
        cur = prev->next;
    }

    return cur;
}

🧠 总结:循环链表做题核心

🎯 关键技巧

复制代码
1️⃣ 一定用 do-while
2️⃣ tail 比 head 更重要
3️⃣ 删除时注意:
    - 是否只剩一个节点
    - 是否删除tail
4️⃣ 多画图(超级关键)
5️⃣ 环形问题优先想到循环链表
相关推荐
阿贵---2 小时前
C++中的RAII技术深入
开发语言·c++·算法
PiKaMouse.2 小时前
navigation2-humble从零带读笔记第一篇:nav2_core
c++·算法·机器人
lightqjx3 小时前
【算法】二分算法
c++·算法·leetcode·二分算法·二分模板
Irissgwe4 小时前
进程间通信
linux·服务器·网络·c++·进程间通信
add45a4 小时前
C++编译期数据结构
开发语言·c++·算法
灰色小旋风4 小时前
力扣21 合并两个有序链表(C++)
c++·leetcode·链表
Laurence4 小时前
Qt 前后端通信(QWebChannel Js / C++ 互操作):原理、示例、步骤解说
前端·javascript·c++·后端·交互·qwebchannel·互操作
王老师青少年编程4 小时前
2026年3月GESP真题及题解(C++五级):有限不循环小数
c++·题解·真题·gesp·csp·五级·有限不循环小数
Amnesia0_04 小时前
C++中的IO流
开发语言·c++