当前位置: 首页 > news >正文

C/C++数据结构之循环链表

概述

        循环链表本质上也是一个单向或双向链表,但其最后一个节点的指针并不指向NULL,而是指向链表的第一个节点,从而形成一个闭合的环。这种结构使得在遍历链表时,可以从任意一个节点开始,并最终回到起始点。

        音乐播放软件一般都提供了重复播放的功能,这意味着:当播放列表中的最后一首歌曲播放完毕后,自动跳转至第一首歌曲继续播放。这种功能可以通过循环链表来轻松实现,其中每首歌曲代表链表中的一个节点。可以看到,循环链表非常适合需要重复访问元素的场景,比如:循环队列、时间轮等。

基本操作

        与单向链表类似,单向循环链表的每个节点包含两部分:一部分是存储数据的数据域,另一部分是指向下一个节点的指针。只不过最后一个节点的pNext指针不为NULL,而是指向头节点。

        对单向循环链表进行插入操作时,如果插入位置在中间,则与单向链表的插入逻辑基本相同。如果在头部插入新节点,则新节点的pNext指向当前头节点,尾部节点的pNext指向新节点,然后更新头指针指向新节点。如果链表为空,则新节点的pNext也指向自己。如果在尾部插入新节点,则首先找到链表的尾节点,即pNext指针指向头节点的那个节点;然后,让它的pNext指向新节点,新节点的pNext指向头节点。

        对单向循环链表进行删除操作时,根据要删除的节点位置,调整前一个节点的pNext指针指向被删节点的下一个节点。如果是删除头节点,则需要更新头指针。

        对单向循环链表进行遍历操作时,可以从任意节点开始,沿着pNext指针移动,直到再次到达起始节点为止。

        具体的实现,可参考下面的示例代码。双向循环链表与双向链表的区别,与上面基本类似,这里就不再赘述了。

#include <iostream>
using namespace std;struct Node
{int nData;Node* pNext;
};// 创建新节点
Node* CreateNode(int nData)
{Node* pNode = new Node();pNode->nData = nData;pNode->pNext = NULL;return pNode;
}// 查找前一个节点
Node* FindPrevious(Node* pHead, Node* pTarget)
{if (pHead == NULL || pTarget == NULL){return NULL;}Node* pCurrent = pHead;while (pCurrent->pNext != pTarget){pCurrent = pCurrent->pNext;if (pCurrent == pHead){// 循环了一圈没找到return NULL;}}return pCurrent;
}// 头部插入
void InsertAtHead(Node*& pHead, int nData)
{Node* pNode = CreateNode(nData);if (pHead == NULL){pHead = pNode;// 循环指向自己pNode->pNext = pHead;}else{// 新节点的pNext指向当前头节点pNode->pNext = pHead;// 尾部节点的pNext指向新节点Node *pPre = FindPrevious(pHead, pHead);if (pPre != NULL){pPre->pNext = pNode;}pHead = pNode;}
}// 尾部插入
void InsertAtTail(Node*& pTail, int nData)
{Node* pNode = CreateNode(nData);if (pTail == NULL){pTail = pNode;pNode->pNext = pTail;}else{pNode->pNext = pTail->pNext;pTail->pNext = pNode;// 更新尾节点为新节点pTail = pNode;}
}// 指定位置后插入
bool InsertAfter(Node* pNode, int nData)
{if (pNode == NULL){return false;}Node* pNewNode = CreateNode(nData);pNewNode->pNext = pNode->pNext;pNode->pNext = pNewNode;return true;
}// 删除操作
bool DeleteNode(Node*& pHead, Node* pDelNode)
{if (pHead == NULL || pDelNode == NULL){return false;}// 只有一个节点的情况if (pDelNode->pNext == pDelNode){delete pDelNode;pHead = NULL;return true;}// 找到前一个节点Node* pPrev = FindPrevious(pHead, pDelNode);if (pPrev == NULL){return false;}// 更新指针pPrev->pNext = pDelNode->pNext;// 如果删除的是头节点,更新head指针if (pDelNode == pHead){pHead = pDelNode->pNext;}delete pDelNode;return true;
}// 遍历操作
void TraverseList(Node* pNode)
{if (pNode == NULL){cout << "List is empty" << endl;return;}Node* pCurrent = pNode;do{cout << pCurrent->nData << " -> ";pCurrent = pCurrent->pNext;} while (pCurrent != pNode);cout << "(back to head)" << endl;
}int main()
{Node* pHead = NULL;Node* pTail = NULL;InsertAtTail(pTail, 77);pHead = pTail;InsertAtTail(pTail, 88);InsertAtHead(pHead, 66);InsertAfter(pTail, 99);// 输出:66 -> 77 -> 88 -> 99 -> (back to head)TraverseList(pHead);return 0;
}

总结

        循环链表是一种非常有用的数据结构,它通过改变传统链表最后一个节点的指针指向,形成了一个闭环,使得数据的循环访问变得简单而高效。

http://www.dtcms.com/a/348194.html

相关文章:

  • Dify 部署+deepseek+python调用(win11+dockerdesktop)
  • 大数据、hadoop、爬虫、spark项目开发设计之基于数据挖掘的交通流量分析研究
  • 【运维进阶】case、for、while、until语句大合集
  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(十七)设置主题
  • CF757F 题解
  • SO_REUSEADDR
  • RuoYi-Vue3项目中Swagger接口测试404,端口问题解析排查
  • 【力扣】2623. 记忆函数——函数转换
  • 硬件抽象层 (HAL, Hardware Abstraction Layer)的简单使用示例
  • 邮箱创建时间打标与自动删除功能设计思路
  • UML时序图中opt,alt,switch-case的问题
  • 用户和组笔记
  • pion/webrtc v4.1.4 版本发布:关键特性与性能优化全面解析
  • 网络协议UDP、TCP
  • maven私服架构
  • Axure RP 9 交互原型设计(Mac 中文)
  • 【实习总结】快速上手Git:关键命令整理
  • 目标检测数据集 第007期-基于yolo标注格式的茶叶病害检测数据集(含免费分享)
  • 深度剖析Spring AI源码(一):蓝图初探,项目结构与设计哲学
  • 【嵌入式开发 Linux 常用命令系列 8 -- git checkout 解冲突详细介绍】
  • 【从零开始学习Redis】如何设计一个秒杀业务
  • [身份验证脚手架] 认证路由 | 认证后端控制器与请求
  • Zabbix 7.0中文乱码矫正
  • 网络协议---TCP
  • 论文阅读:VACE: All-in-One Video Creation and Editing
  • 机器学习算法-朴素贝叶斯
  • k8sday16调度器
  • Java全栈工程师面试实战:从基础到微服务的深度解析
  • 【运维进阶】高可用和负载均衡技术
  • 港口集装箱编号识别误识率↓79%!陌讯多模态融合算法落地优化