跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
GSXAB的知识库
搜索
搜索
外观
登录
个人工具
登录
Advertising:
查看“︁单向循环链表”︁的源代码
页面
讨论
简体中文
阅读
查看源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
查看源代码
查看历史
刷新
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
←
单向循环链表
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[分类:线性表]] [[分类:链式存储结构]] {{InfoBox |name=单向循环链表 |eng_name=circular singly linked list }} {{#seo: |keywords=单向循环链表,循环单链表,循环链表 |description=介绍了单向循环链表,即逻辑结构为线性表、存储结构为链式、结点拥有指向下一结点链接且尾结点链接指向头部的数据结构,即结点拥有指向下一结点链接且尾结点链接指向头部的链表。 |modified_time={{REVISIONYEAR}}-{{REVISIONMONTH}}-{{REVISIONDAY2}} |published_time=2025-7-17 }} '''单向循环链表'''('''circular singly linked list''')指逻辑结构为[[线性表]]、存储结构为[[链式存储结构]](即属于[[链表]]),且结点间只存在指向下个结点的链接(尾结点除外)的数据结构。其是[[单向链表]]变体,尾结点中的指针不再为空,而是指向第一个数据结点,如果有头结点(哨兵结点)则指向头结点,形成一个循环的结构。 与单向链表类似,在内存中,链表分配和访问内存的方式不符合[[访问局部性原理]],对[[缓存]]不友好,执行效率会按照缓存与内存的访问时间比例成数量级地放大。因此真正需要链表时往往通过[[内存池]]方案预分配结点块,或者结合[[自由链表]]的形式复用存储上局部化的结点,减少内存碎片。 与单向链表不同的是,由于链接成环,单向循环链表可以从其中任意一个结点开始循环遍历全部结点。如果需要较高性能,且结构上不需要具体的头尾,甚至可以进一步将尾结点的下一链接指向上一结点,留下一个指向环中任意位置的指针,并忽略头尾将所有结点视为有着循环前后顺序的的结点。 == 结构 == 单向循环链表中的结点与普通单向链表相同,可以抽象成如图所示结构。 {{GiteaSvg|singly_linked_node}} 其中含有数据域和指向下个结点的链接。链接成一个链表时如下图所示,较普通的单向链表增加了一个从最后一个结点指回第一个结点的指针。 {{GiteaSvg|circular_singly_linked_list}} 与单向链表相似,循环链表通常保存一个'''头指针'''('''head pointer''', '''head reference''')或加入一个'''头结点'''('''head node''')/'''哨兵结点'''('''sentinel node''')。在加入头结点情况下,尾结点的指针也指向头结点。 == 语言实例 == {{数据结构实例 |c=人工实现 |cpp=人工实现 }} == 复杂度估计 == === 空间复杂度 === 单向循环链表整体空间复杂度为[[线性复杂度]],每个结点保存一个数据,使得结点数量与数据量成正比。但是由于结点中需要额外保存链接,比起直接保存有额外常数,考虑每段数据的大小和链接大小的关系,为 <math>\sim \frac{d+p}{d} n=O(n)</math> 。 === 时间复杂度估计与优化 === 本节讨论单向链表对其逻辑结构线性表的操作实现。 ==== 插入删除操作 ==== 在链表两端的插入算法通常称为[[头插法]]和[[尾插法]]。这两个词一般指构造时的算法,但也常用在向已有链表插入结点的场景。 插入删除时,涉及是否允许从尾部插入删除、是否保存并维护尾部指针,也存在一些优化差异。 若需要进行尾插法,实现上可以保存尾指针,在尾插法中不需要提前找到尾部,直接操作尾部(但尾插法作为构造链表过程时是自动获得尾部的,不涉及这个问题), 但是在删除尾结点时,需要将尾指针移动到上一个结点,这就不得不从头进行遍历寻找; 相反,若实现不保存尾指针,则尾部插入删除都需要额外遍历。 ==== 时间复杂度总结 ==== 记链表长度为 <math>n</math> 。 {| class='wikitable' style='width:100%' ! colspan=2 | 操作 ! 最好 ! 最坏 ! 平均 |- ! rowspan=3 | 基本操作 ! 初始化 <code>Create</code> | colspan=3 | 不初始化结点为一次分配 |- ! 长度 <code>Length/Size</code> | colspan=3 | <math>O(n)</math> ,如果没维护则需要遍历 |- ! 清空 | colspan=3 | <math>O(n)</math> ,逐个结点销毁 |- ! rowspan=4 | 元素获取 ! 指定索引 <code>Get(i)</code> | <math>\Theta(1)</math> ,起始位置 | <math>\Theta(n)</math> ,结束位置 | <math>\sim i\sim \tfrac{n}{2}=O(n)</math> ,需要遍历到指定位置 |- ! 指定位置 <code>Get(p)</code> | colspan=3 | <math>\Theta(1)</math> |- ! 头部 <code>Head</code> | colspan=3 | <math>\Theta(1)</math> |- ! 尾部 <code>Rear</code> | colspan=3 | <math>\Theta(n)</math> ,若需要遍历到尾部 |- ! rowspan=4 | 插入删除 ! 指定索引 <code>Insert(i,x)/Remove(i)</code> | <math>\Theta(1)</math> ,头部 | <math>\Theta(n)</math> ,尾部 | <math>\sim i\sim \tfrac{n}{2}=\Theta(n)</math> ,平均遍历长度 |- ! 指定位置 <code>InsertAfter(p,x)/RemoveAfter(p)</code> * | colspan=3 | <math>\Theta(1)</math> ,只需要插入删除结点后连接指针 |- ! 头部 | colspan=3 | <math>\Theta(1)</math> |- ! 尾部 | colspan=3 | <math>\Theta(n)</math> ,需要遍历找到尾部或其前结点 |- ! rowspan=2 | 元素顺序 ! 指定位置前趋 <code>Pred</code> | <math>\Theta(1)</math> ,头部 | <math>\Theta(n)</math> ,尾部 | <math>\sim i\sim \tfrac{n}{2}=\Theta(n)</math> ,平均遍历长度 |- ! 指定位置后继 <code>Succ</code> | colspan=3 | <math>\Theta(1)</math> ,只需要随着链接找到 |} * 由于单向循环链表中直接操作一个结点往往需要修改前后结点的指针,一般指定其前一结点的位置而不是本结点,根据链表连接关系得到本结点和下一结点,所以和线性表词条中的定义不同。 {{数据结构}}
返回
单向循环链表
。
Advertising: