数据结构 | 单循环链表

一、认识单循环链表

单循环链表是带头结点的单链表 + 闭环设计,尾节点不再指向 NULL,而是回头指向头结点,形成一个首尾相连的环。

这种结构最大的优势:可以从任意节点遍历全表,不再需要判断 NULL,逻辑更统一。

1. 节点结构体设计思路

循环链表节点结构与单链表完全一致,只改变指针指向规则,不改变节点本身。

cpp 复制代码
struct CNode {
    int data;               // 数据域:存储有效信息
    CNode* next;            // 指针域:指向下一个节点
};

2. 单循环链表 vs 普通单链表

  • 尾节点指向:单链表→NULL;循环链表→头结点
  • 判空条件:单链表 head->next==NULL;循环链表 head->next==head
  • 遍历结束:单链表 p==NULL;循环链表 p==head

【单链表 结构示意图】

【单循环链表 结构示意图】

二、基础工具函数

基础函数是插入、删除的前提,包含:初始化、判空、查找、获取长度、打印。

1. 初始化

思路:空表时,头结点指向自己,形成最小闭环。

cpp 复制代码
// 初始化带头结点的单循环链表
void Init_CircleList(CNode* plist) {
    if (plist == nullptr) return;
    plist->next = plist;       // 空表:头结点指向自身
}

2. 判空

cpp 复制代码
bool Is_Empty(CNode* plist) {
    return plist->next == plist;
}

3. 查找

cpp 复制代码
CNode* Search(CNode* plist, int val) {
    if (plist == nullptr) return nullptr;
    CNode* p = plist->next;
    // 遍历一圈:回到头结点结束
    while (p != plist) {
        if (p->data == val) return p;
        p = p->next;
    }
    return nullptr;
}

4. 获取有效长度

cpp 复制代码
int Get_Length(CNode* plist) {
    int cnt = 0;
    CNode* p = plist->next;
    while (p != plist) {
        cnt++;
        p = p->next;
    }
    return cnt;
}

5. 打印链表

cpp 复制代码
void Show(CNode* plist) {
    if (Is_Empty(plist)) {
        cout << "链表为空!" << endl;
        return;
    }
    CNode* p = plist->next;
    while (p != plist) {
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}

三、插入操作

按位置插入: 头插、尾插都是它的特殊情况

  • 头插 → pos = 0
  • 尾插 → pos = 链表长度
  • 中间插 → 0 < pos < 长度

插入思路

  1. 判断位置合法性

  2. 找到前驱节点

  3. 新建节点

  4. 先连后面,再连前面(防止指针断裂)

【头插示意图】

【尾插示意图】

cpp 复制代码
// 按位置插入(统一头插、尾插、中间插)
bool Insert_Pos(CNode* plist, int val, int pos) {
    int len = Get_Length(plist);
    if (pos < 0 || pos > len) return false;
    // 新建节点
    CNode* newNode = new CNode;
    newNode->data = val;
    // 找前驱
    CNode* p = plist;
    for (int i = 0; i < pos; i++) {
        p = p->next;
    }
    // 插入(核心步骤)
    newNode->next = p->next;
    p->next = newNode;
    return true;
}

四、删除操作

删除操作统一为两个核心函数,所有删除场景都能覆盖:

1. 按位置删除

  • 头删 → pos = 0
  • 尾删 → pos = len - 1
  • 中间删 → 通用

【头删示意图】

【尾删示意图】

cpp 复制代码
// 按位置删除
bool Del_Pos(CNode* plist, int pos) {
    if (Is_Empty(plist)) return false;
    int len = Get_Length(plist);
    if (pos < 0 || pos >= len) return false;
    // 找前驱
    CNode* p = plist;
    for (int i = 0; i < pos; i++) {
        p = p->next;
    }
    // 待删除节点
    CNode* q = p->next;
    // 跨越指向
    p->next = q->next;
    // 释放内存
    delete q;
    return true;
}

2. 按值删除

cpp 复制代码
// 按值删除
bool Del_Val(CNode* plist, int val) {
    if (Is_Empty(plist)) return false;
    // 第一步:找目标节点
    CNode* p = plist->next;
    while (p != plist) {
        if (p->data == val) break;
        p = p->next;
    }
    if (p == plist) return false; // 没找到
    // 第二步:找前驱
    CNode* q = plist;
    while (q->next != p) {
        q = q->next;
    }
    // 第三步:删除
    q->next = p->next;
    delete p;
    return true;
}

五、清空与销毁

核心区别

清空 :删除所有数据节点,保留头结点,链表还能继续用

销毁 :删除所有节点(含头结点),链表彻底销毁

销毁函数

cpp 复制代码
// 销毁整个循环链表
void Destroy(CNode*& plist) {
    CNode* p = plist->next;
    CNode* q = nullptr;
    // 释放所有数据节点
    while (p != plist) {
        q = p->next;
        delete p;
        p = q;
    }
    // 最后释放头结点
    delete plist;
    plist = nullptr;
}

六、总结

  1. 闭环:尾节点指向头结点,无 NULL 指针。

  2. 判空 :head->next == head;遍历结束:p == head。

  3. 插入/删除统一:只需要按位置操作,头插尾插、头删尾删都是特例。

  4. 指针修改原则:先连后面,再连前面,避免环断裂。

  5. 内存安全:new 必须配套 delete,销毁时必须释放所有节点。

  6. 应用场景:环形队列、循环播放列表、轮询调度等需要循环遍历的场景。

相关推荐
折哥的程序人生 · 物流技术专研7 小时前
Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)
java·网络·数据库·算法·面试
想吃火锅10058 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展
云絮.10 小时前
数据库操作
数据库·mysql·算法·oracle
小林ixn10 小时前
LeetCode 206. 反转链表(迭代 + 递归详解)
算法·leetcode·链表
凡人叶枫10 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
菜鸟‍11 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展
退休倒计时13 小时前
【每日一题】LeetCode 142. 环形链表 II TypeScript
算法·leetcode·链表·typescript
popcorn_min13 小时前
Digits 手写数字识别:随机森林多分类 + 像素级特征热力图
算法·随机森林·分类
liulilittle14 小时前
拥塞控制:排水终止的两种决策:OR 与 AND
网络·tcp/ip·计算机网络·算法·信息与通信·tcp·通信
花间相见14 小时前
【LeetCode02】—— 两数之和:哈希表入门经典详解
数据结构·散列表