文章目录

  • 1.概念
  • 2.循环链表的头文件与函数声明
  • 3.函数实现
    • 3.1 购买节点
    • 3.2 初始化函数(重点)
    • 3.3 判空函数
    • 3.4 获取有效数据个数
    • 3.5 头插函数
    • 3.6尾插(重点)
    • 3.7 按位置插入
    • 3.8 头删
    • 3.9 尾删(重点)
    • 3.10 按位置删除节点
    • 3.11 查找
    • 3.12 按值删除
    • 3.13 销毁
      • 3.13.1 销毁1
      • 3.13.2 销毁2
  • 4.循环链表的源文件与函数实现

1.概念

循环链表:和单链表唯一的区别:尾节点保留着头节点的地址


循环链表的特点:

1. 尾节点可以找到头节点
2. 头节点不能找到尾节点
3. 如果没有有效数据节点(空链),头节点指向自身

注意,循环链表,for循环去遍历,判断表达式不再是p->next!=NULL,而是p->next!=plist

2.循环链表的头文件与函数声明

#pragma oncetypedef int ELEM_TYPE;
//循环链表有效数据节点结构体设计:typedef struct CNode
{ELEM_TYPE data;//数据域struct CNode* next;//指针域
}CNode, *PCNode;//有关循环链表的操作函数://初始化
void Init_clist(struct CNode* plist);//购买节点
struct CNode* BugNode(ELEM_TYPE val);//头插
bool Insert_head(PCNode plist, ELEM_TYPE val);//尾插
bool Insert_tail(PCNode plist, ELEM_TYPE val);//按位置插  pos=0  头插
bool Insert_pos(PCNode plist, int pos, ELEM_TYPE val);//头删
bool Del_head(PCNode plist);//尾删
bool Del_tail(PCNode plist);//按位置删  pos=0  头删
bool Del_pos(PCNode plist, int pos);//按值删除
bool Del_val(PCNode plist, ELEM_TYPE val);//查找
struct CNode* Search(PCNode plist, ELEM_TYPE val);//判空
bool Is_Empty(PCNode plist);
//判满  循环链表也不需要判满//获取有效元素个数
int Get_length(PCNode plist);//清空
void Clear(PCNode plist);//销毁1
void Destroy(PCNode plist);//销毁2
void Destroy2(PCNode plist);//打印
void Show(PCNode plist);

3.函数实现

3.1 购买节点

从堆区申请一块内存,返回给指针

//购买节点
struct CNode* BuyNode(ELEM_TYPE val)
{//assertstruct CNode* pnewnode = (struct CNode*)malloc(1 * sizeof(struct CNode));assert(pnewnode != NULL);if(pnewnode == NULL){return NULL;}pnewnode->data = val;pnewnode->next = NULL;return pnewnode;
}

3.2 初始化函数(重点)

使头节点的next域指向自身(单链表是指向NULL)

void Init_clist(struct CNode* plist)
{//assert//plist->data;   数据域不需要处理//plist->next = NULL;//单链表的写法plist->next = plist;//循环链表的写法   }

3.3 判空函数

如果头节点的next域等于自身的话,就代表着链表为NULL;

void Init_clist(struct CNode* plist)
{//assert//plist->data;   数据域不需要处理//plist->next = NULL;//单链表的写法plist->next = plist;//循环链表的写法   }

3.4 获取有效数据个数

利用一个指针 p,从第一个有效数据节点开始遍历,每次遍历计数器+1;
直到 p 等于头指针结束,返回计数器的值

//获取有效元素个数
int Get_length(PCNode plist)
{//assertint count = 0;for(struct CNode *p=plist->next; p!=plist; p=p->next){count++;}return count;
}

3.5 头插函数

直接使用单链表头插法
首先通过 **malloc()**函数申请一个节点,保存val值
然后把头节点指向的next的地址赋值给 s->next
最后 把s 的地址赋值给头节点的next域

//头插
bool Insert_head(PCNode plist, ELEM_TYPE val)
{//assert//1.购买新节点struct CNode *pnewnode = BuyNode(val);assert(pnewnode != NULL);if(pnewnode == NULL){return false;}//2.找到合适的插入位置  头插不需要特意去找合适的插入位置//3.插入pnewnode->next = plist->next;plist->next = pnewnode;return true;
}

3.6尾插(重点)

首先用指针p遍历循环链表,使p指向尾节点的位置,然后用**buynode()**函数申请一个节点
此时的 p->next 是头节点
我们把 p->next 赋值给 新节点的next域

然后把s的地址赋值给 p->next 域 完成尾插

//尾插
bool Insert_tail(PCNode plist, ELEM_TYPE val)
{//assert//1.购买新节点struct CNode *pnewnode = BuyNode(val);assert(pnewnode != NULL);if(pnewnode == NULL){return false;}//2.找到合适的插入位置struct CNode* p;for(p=plist; p->next!=plist; p=p->next);//用上面的for循环  让p停留在尾结点//此时 p指向尾结点//3.插入pnewnode->next = p->next;p->next =pnewnode;return true;
}

3.7 按位置插入

首先判断 pos位置的合法性,pos必须大于等于0 且pos 不能大于链表有效值个数
然后按照单链表方式插入

//按位置插  pos=0  头插
bool Insert_pos(PCNode plist, int pos, ELEM_TYPE val)
{assert(plist != NULL);//保证循环链表存在assert(pos >=0 && pos<=Get_length(plist));//1.购买新节点struct CNode *pnewnode = BuyNode(val);assert(pnewnode != NULL);if(pnewnode == NULL){return false;}//2.找到合适的插入位置struct CNode *p = plist;for(int i=0; i<pos; i++){p=p->next;}//此时 p指向待插入位置的前一个节点//3.插入pnewnode->next = p->next;p->next = pnewnode;return true;
}

3.8 头删

先判空,然后删除链表中第一个有效数据节点
方法如同单链表

//头删
bool Del_head(PCNode plist)
{//assert//删除需要判空 if(Is_Empty(plist)){return false;}struct CNode *p = plist->next;plist->next = p->next;//plist->next = plist->next->next;free(p);return true;
}

3.9 尾删(重点)

使用两个指针 分别向后跑 一个指向最后一个节点,一个指向最后一个节点的前一个节点,有两种方案:
第一种:
先找p,再找q,先让p遍历链表直到 p->next==plist 说明p已经到了尾节点
然后让q遍历链表,直到 q->next == p ,此时q指向的是尾指针的前一个节点
然后使倒数第二个节点的next 指向头指针 plist ,然后释放p(曾经的尾节点)

//尾删
bool Del_tail(PCNode plist)
{//assert plist!=NULLif(Is_Empty(plist)) //如果不空,那至少有一个有效数据{return false;}//让p和q找到合适的位置:/*//第一种方案:先找p,再找qstruct CNode *p = plist;for(p; p->next!=plist; p=p->next);//此时for循环结束,p指向尾结点struct CNode *q= plist;for(q; q->next!=p; q=q->next);//此时for循环结束,q在p前面*///第二种方案:先找q,再直接让p=q->next即可struct CNode *q = plist;for(q; q->next->next!=plist; q=q->next);//q->  和 q->next->  没有风险,前面排除过了//此时for循环结束,q在倒数第二个节点struct CNode *p = q->next; //跨越指向,再接着释放待删除节点q->next = p->next;//q->next = q->next->next;free(p);return true;}

3.10 按位置删除节点

首先判断 pos位置的合法性,pos必须大于等于0 且pos 不能大于链表有效值个数
然后按照单链表方式删除

//按位置删  pos=0  头删
bool Del_pos(PCNode plist, int pos)
{assert(plist != NULL);assert(pos >=0 && pos<Get_length(plist));if(Is_Empty(plist)){return false;}//让p和q找到合适的位置:struct CNode *q = plist;for(int i=0; i<pos; i++){q = q->next;}struct CNode *p = q->next; //跨越直线,释放待删除节点q->next = p->next;free(p);return true;
}

3.11 查找

遍历链表,与单链表相同

//查找
struct CNode* Search(PCNode plist, ELEM_TYPE val)
{//assertfor(struct CNode *p=plist->next; p!=plist; p=p->next){if(p->data == val){return p;}}return NULL;
}

3.12 按值删除

先调用 **Search()**找到要删除节点的地址
然后删除,与单链表方法相同

//按值删除
bool Del_val(PCNode plist, ELEM_TYPE val)
{//assertif(Is_Empty(plist)){return false;}struct CNode *p = Search(plist, val);if(p == NULL){return false;}//此时,如果p不为NULL,代表val值存在,且p现在指向它//在让q找到p的前一个节点  struct CNode *q = plist;for(q; q->next!=p; q=q->next);//跨越指向,释放待删除节点q->next = p->next;free(p);return true;
}

3.13 销毁

3.13.1 销毁1

借助头节点进行无限头删,直到头节点的plist->next = plist
类似单链表

void Destroy(PCNode plist)
{//assertwhile(plist->next != plist){struct CNode *p = plist->next;plist->next = p->next;free(p);}plist->next = plist;
}

3.13.2 销毁2

提前踢出去头节点,借助两个临时指针
使p指向第一个节点,ps->next = NULL

当p!=plist的时候 使q = p->next; 然后释放p

然后使 p = q

此时完成一次循环
再使 p = p->next; free(q);

直到 p ==plist 的时候结束循环;此时销毁完成

//销毁2(不借助头结点,有两个临时指针)
void Destroy2(PCNode plist)
{//assert plsit!=NULLstruct CNode *p = plist->next; struct CNode *q = NULL;plist->next = plist;while(p != plist){q = p->next;free(p);p = q;}
}

4.循环链表的源文件与函数实现

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "clist.h"/*
注意事项:
1.尾可以找到头,但是头不能找到尾
2.如果循环链表是一个空链,头结点的指针域指向自身
3.注意,循环链表,for循环去遍历,判断表达式不再是p->next!=NULL,而是p->next!=plist
*/
/*
for(struct Node* p=plist; p->next!=NULL; p=p->next)//上面for循环用于需要前驱的操作:比如插入删除
for(struct Node* p=plsit->next; p!=NULL; p=p->next)//下面for循环用于不需要前驱的操作:比如查找,找有效值个数,打印
*///有关循环链表的操作函数:
//初始化
void Init_clist(struct CNode* plist)
{//assert//plist->data;   数据域不需要处理//plist->next = NULL;//单链表的写法plist->next = plist;//循环链表的写法   }//购买节点
struct CNode* BuyNode(ELEM_TYPE val)
{//assertstruct CNode* pnewnode = (struct CNode*)malloc(1 * sizeof(struct CNode));assert(pnewnode != NULL);if(pnewnode == NULL){return NULL;}pnewnode->data = val;pnewnode->next = NULL;return pnewnode;
}//头插
bool Insert_head(PCNode plist, ELEM_TYPE val)
{//assert//1.购买新节点struct CNode *pnewnode = BuyNode(val);assert(pnewnode != NULL);if(pnewnode == NULL){return false;}//2.找到合适的插入位置  头插不需要特意去找合适的插入位置//3.插入pnewnode->next = plist->next;plist->next = pnewnode;return true;
}//尾插
bool Insert_tail(PCNode plist, ELEM_TYPE val)
{//assert//1.购买新节点struct CNode *pnewnode = BuyNode(val);assert(pnewnode != NULL);if(pnewnode == NULL){return false;}//2.找到合适的插入位置struct CNode* p;for(p=plist; p->next!=plist; p=p->next);//用上面的for循环  让p停留在尾结点//此时 p指向尾结点//3.插入pnewnode->next = p->next;p->next =pnewnode;return true;
}//按位置插  pos=0  头插
bool Insert_pos(PCNode plist, int pos, ELEM_TYPE val)
{assert(plist != NULL);//保证循环链表存在assert(pos >=0 && pos<=Get_length(plist));//1.购买新节点struct CNode *pnewnode = BuyNode(val);assert(pnewnode != NULL);if(pnewnode == NULL){return false;}//2.找到合适的插入位置struct CNode *p = plist;for(int i=0; i<pos; i++){p=p->next;}//此时 p指向待插入位置的前一个节点//3.插入pnewnode->next = p->next;p->next = pnewnode;return true;
}
//头删
bool Del_head(PCNode plist)
{//assert//删除需要判空 if(Is_Empty(plist)){return false;}struct CNode *p = plist->next;plist->next = p->next;//plist->next = plist->next->next;free(p);return true;
}//尾删
bool Del_tail(PCNode plist)
{//assert plist!=NULLif(Is_Empty(plist)) //如果不空,那至少有一个有效数据{return false;}//让p和q找到合适的位置:/*//第一种方案:先找p,再找qstruct CNode *p = plist;for(p; p->next!=plist; p=p->next);//此时for循环结束,p指向尾结点struct CNode *q= plist;for(q; q->next!=p; q=q->next);//此时for循环结束,q在p前面*///第二种方案:先找q,再直接让p=q->next即可struct CNode *q = plist;for(q; q->next->next!=plist; q=q->next);//q->  和 q->next->  没有风险,前面排除过了//此时for循环结束,q在倒数第二个节点struct CNode *p = q->next; //跨越指向,再接着释放待删除节点q->next = p->next;//q->next = q->next->next;free(p);return true;}//按位置删  pos=0  头删
bool Del_pos(PCNode plist, int pos)
{assert(plist != NULL);assert(pos >=0 && pos<Get_length(plist));if(Is_Empty(plist)){return false;}//让p和q找到合适的位置:struct CNode *q = plist;for(int i=0; i<pos; i++){q = q->next;}struct CNode *p = q->next; //跨越直线,释放待删除节点q->next = p->next;free(p);return true;
}//按值删除
bool Del_val(PCNode plist, ELEM_TYPE val)
{//assertif(Is_Empty(plist)){return false;}struct CNode *p = Search(plist, val);if(p == NULL){return false;}//此时,如果p不为NULL,代表val值存在,且p现在指向它//在让q找到p的前一个节点  struct CNode *q = plist;for(q; q->next!=p; q=q->next);//跨越指向,释放待删除节点q->next = p->next;free(p);return true;
}//查找
struct CNode* Search(PCNode plist, ELEM_TYPE val)
{//assertfor(struct CNode *p=plist->next; p!=plist; p=p->next){if(p->data == val){return p;}}return NULL;
}//判空
bool Is_Empty(PCNode plist)
{return plist->next == plist;
}
//判满  循环链表也不需要判满//获取有效元素个数
int Get_length(PCNode plist)
{//assertint count = 0;for(struct CNode *p=plist->next; p!=plist; p=p->next){count++;}return count;
}//清空
void Clear(PCNode plist)
{Destroy(plist);
}//销毁1(借助头结点,无限头删)
void Destroy(PCNode plist)
{//assertwhile(plist->next != plist){struct CNode *p = plist->next;plist->next = p->next;free(p);}plist->next = plist;
}//销毁2(不借助头结点,有两个临时指针)
void Destroy2(PCNode plist)
{//assert plsit!=NULLstruct CNode *p = plist->next; struct CNode *q = NULL;plist->next = plist;while(p != plist){q = p->next;free(p);p = q;}
}//打印
void Show(PCNode plist)
{//assertfor(struct CNode *p=plist->next; p!=plist; p=p->next){printf("%d ", p->data);}printf("\n");}

【数据结构】循环链表相关推荐

  1. 数据结构——循环链表之约翰夫生死游戏

    数据结构--循环链表之约翰夫生死游戏 一.什么是约翰夫生死游戏? 约瑟夫游戏的大意是: 每30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分;因此船长告诉乘客,只有将全船一半的旅客投入还中,其 ...

  2. 基本数据结构----循环链表

    循环链表是一种链式存储结构,它的最后一个节点指向头结点,形成一个环.因此,从循环链表中的任何一个结点出发都能找到任何其他结点.循环链表的操作和单链表的操作基本一致,差别仅仅在于算法中的循环条件有所不同 ...

  3. python算法与数据结构-循环链表

    求链表长度 需要考虑的特殊情况,空链表或者链表当中只有一个节点的时候. 添加元素: 除了头部指向新节点,单向循环链表也需要尾节点指向新的节点 删除元素如下所示: 代码如下所示: # -*-coding ...

  4. python算法与数据结构-循环链表(41)

    阅读目录 一.循环链表的介绍 二.循环链表基本操作的python代码实现 三.循环链表基本操作的C语言实现 一.循环链表的介绍 上一篇我们已经讲过单链表,本篇给大家讲解循单链表的一个变形是单向循环链表 ...

  5. Linux C 数据结构—-循环链表

    前面我们学习了单向链表,现在介绍单向循环链表,单向循环链表是单链表的一种改进,若将单链表的首尾节点相连,便构成单向循环链表结构,如下图: 对于一个循环链表来说,其首节点和末节点被连接在一起.这种方式在 ...

  6. 数据结构 循环链表及其基本操作

    目录 一.引入 二.循环链表的定义 三.单链表与循环链表对比 3.1图示对比 3.2代码对比 四.循环链表的操作 4.1循环链表的初始化 4.2循环链表的建立 4.2.1头插法建立循环链表 4.2.2 ...

  7. 数据结构——循环链表

    1.介绍 循环链表,故名思意,就是首尾相连的一个链表. 对于单链表来说,最后一个结点的指针不为nullptr,而是头指针,即存放头节点的地址. 对于双链表来说,头节点的prior指针指向最后一个结点, ...

  8. 头歌JAVA数据结构答案

    头歌JAVA数据结构答案 一.Java数据结构-循环链表的设计与实现 第1关 单循环链表的实现-链表的添加.遍历 package step1; /*** Created by sykus on 201 ...

  9. 数据结构与算法--头歌(educoder)实训作业题目及答案

    目录 Java 数据结构之图 第1关:图的表示 第2关:深度优先搜索 第3关:广度优先搜索 第4关:单源最短路径 Java 数据结构之排序 第1关:选择排序 第2关:插入排序 第3关:归并排序 第4关 ...

  10. [总结] 全部笔记博文目录总结(持续更新...)

    计算机网络 [Linux] tcpdump 过滤传递给指定端口的数据 [Linux] 使用tcpkill杀掉tcp连接 [Linux]利用tcpdump打印sql语句 [linux] tcpdump抓 ...

最新文章

  1. 华为手机鸿蒙系统名单,鸿蒙系统:前三批升级名单曝光,有荣耀手机!
  2. N皇后问题的两个最高效的算法
  3. 仓库如何盘点 打印扫描一体PDA盘点机提升库存盘点效率
  4. 图形验证码校验php,ThinkPHP实现生成和校验验证码功能
  5. 云服务器cpu性能,云服务器cpu性能
  6. 墨刀原型制作 位置选择_原型制作不再是可选的
  7. 【华为云技术分享】云图说|华为HiLens:端云协同多模态AI开发应用平台
  8. python 基础及资料汇总
  9. 网站爬虫Python
  10. [Zinnia][Windows]手写输入法的一些研究
  11. 神通数据库导出sql脚本
  12. element-ui el-descriptions取消冒号
  13. Linux——虚拟内存空间分布
  14. NSIS 头文件介绍_TextFunc.nsh(2)
  15. win11 任务栏角溢出里的程序图标记录如何删除(WIN 缓存图标清理)
  16. spring源码解析--环境搭建
  17. 苹果Airplay协议以及AirTunes流程总结
  18. 编译原理_P1004
  19. 三年级神奇电子计算机教案,人教版三年级上册信息技术教案
  20. 万兴PDF程序安装及注意事项

热门文章

  1. 德馨食品启动上市辅导:中信资本持股10%,曾因食品安全问题被罚
  2. prue view 函数需要注意的点
  3. MySQL日期格式化函数 DATE_FORMAT
  4. 0基础学RS(十九)以太网通道链路聚合
  5. http etag java_你应该知道的HTTP头------ETag
  6. java数据脱敏处理,手机号,身份证号和银行卡号打码展示
  7. 编写一个实现串的置换操作Replace(S,T,V)
  8. 第八章 使用Spring Web Flow
  9. 【Java 8 新特性】Java Comparator | 比较器
  10. 详细描述磁浮列车电液混合制动方法的离散特性