数据结构 | 单循环链表

一、认识单循环链表

单循环链表是带头结点的单链表 + 闭环设计,尾节点不再指向 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. 应用场景:环形队列、循环播放列表、轮询调度等需要循环遍历的场景。

相关推荐
小π军12 小时前
最大重叠区间数量
数据结构·算法
镜像视界(浙江)科技有限公司12 小时前
《视频孪生之上:二维展示终结,三维空间计算重构城市逻辑》——跨摄像连续表达 × 三角测量厘米级定位 × 动态轨迹建模,构建新一代城市空间
大数据·人工智能·算法·矩阵·音视频·空间计算
a1879272183113 小时前
【算法】双指针与滑动窗口(二):滑动窗口——吃进、判定、吐出
算法·leetcode·双指针·滑动窗口·原理·模板·算法讲解
铭哥的编程日记13 小时前
从一道 LeetCode Hard 到吃透一类题:加权区间调度「排序 + 二分 + DP」
算法·leetcode·职场和发展
Logic10113 小时前
C语言/数据结构字符串题解:找出DNA序列中未配对的独特序列——排序+遍历(O(nlogn))
c语言·数据结构·字符串·数组·排序·时间复杂度·算法题
Navigator_Z13 小时前
LeetCode //C - 1248. Count Number of Nice Subarrays
c语言·算法·leetcode
wzdark14 小时前
基于链表的内存池设计与内存复用机制4
java·数据结构·链表
wzdark14 小时前
多维数组在算法设计中的存储映射问题4
算法
Phil32315 小时前
多智能体不是越多越好:Google《Towards a Science of Scaling Agent Systems》论文深度解读
算法
6Hzlia15 小时前
【Classic 150 刷题计划】 LeetCode 26. 删除有序数组中的重复项 | C++ 快慢双指针经典模板
c++·算法·leetcode