单向循环链表
单向循环链表
- 循环链表的主要特征就是没有NULL,头结点需要自己指向自己

- 链表的节点
//存储数据类型(可修改),在遍历数据的时候是需要输出的,所以输出类型也需要修改
//链表节点下面是数据的数据类型
typedef int DataType_t;
//带头结点的链表
//1.每一个节点都有自己的元素和连接下一个元素的地址
typedef struct circularlinklist
{
DataType_t data; //链表节点存放的数据
struct circularlinklist *next; //链表节点存放下一个链表节点的地址
}CclLkList_t;
创建链表
/***************************************************
* 函数名称: CclLkList_CreatList
* 函数功能: 创建一个新的单向循环链表
* 函数参数: None
* 返回结果: 返回链表的地址:创建成功。NULL:创建失败。
* 注意事项: None
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
CclLkList_t * CclLkList_CreatList(void)
{
//在堆空间申请一个空间存放链表的头结点
CclLkList_t * Head = (CclLkList_t *)calloc(1,sizeof(CclLkList_t));
if(NULL == Head){
perror("calloc memory for head is failed!!!");
return NULL;
}
//头结点的初始化,数据不需要初始化,单向循环链表的头结点创建时要指向自己
Head->next = Head;
//返回头结点的地址
return Head;
}
创建新结点
/***************************************************
* 函数名称: CclLkList_NewNode
* 函数功能: 创建一个链表新的节点
* 函数参数:
* @data :创建链表节点时,下面存放的数值
* 返回结果: 返回链表的地址:创建成功。NULL:创建失败。
* 注意事项: 一般都在插入的时候需要使用,不会单独使用
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
CclLkList_t * CclLkList_NewNode(DataType_t data)
{
//1.先从堆空间申请内存存放链表节点
CclLkList_t * New = (CclLkList_t *)calloc(1,sizeof(CclLkList_t));
if(NULL == New){
perror("Calloc memory for NewNode is failed!");
return NULL;
}
//初始化链表节点,节点也要指向自己
New->data = data;
New->next = New;
//申请成功直接返回
return New;
}
头部插入结点

/***************************************************
* 函数名称: CclLkList_HeadInsertNode
* 函数功能: 在链表的头结点后面插入节点
* 函数参数:
* @Head :要插入链表的地址
* @data :插入的数据
* 返回结果: true:插入成功。false:插入失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_HeadInsertNode(CclLkList_t *Head,DataType_t data)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//创建新的节点
CclLkList_t *New = CclLkList_NewNode(data);
if(NULL == New){
return false;
}
//头结点只是开头不存放数据,循环的尾结点的next要指向首节点,只有头结点的话他是自己指向自己,所以要特殊处理
//需要先判断链表是否为空(只有头结点)
if(Head == Head->next){
//为空的话另外插入
Head->next = New;
return true;
}
//不为空
//备份头结点去找尾结点
CclLkList_t *Phead = Head->next;
while(Phead->next != Head->next){ /* 尾结点的next指向首节点 */
Phead = Phead->next;
}
//现在可以开始插入
New->next = Head->next;
Phead->next = New;
Head->next = New;
return true;
}
尾部插入结点

/***************************************************
* 函数名称: CclLkList_TailInsertNode
* 函数功能: 在链表的尾结点后面插入节点
* 函数参数:
* @Head :要插入链表的地址
* @data :插入的数据
* 返回结果: true:插入成功。false:插入失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_TailInsertNode(CclLkList_t *Head,DataType_t data)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//创建新的节点
CclLkList_t *New = CclLkList_NewNode(data);
if(NULL == New){
return false;
}
//头结点只是开头不存放数据,循环的尾结点的next要指向首节点,只有头结点的话他是自己指向自己,所以要特殊处理
//需要先判断链表是否为空(只有头结点)
if(Head == Head->next){
//为空的话另外插入
Head->next = New;
return true;
}
//不为空
//备份头结点去寻找尾部
CclLkList_t *Phead = Head->next;
while(Phead->next != Head->next){
Phead = Phead->next;
}
//寻找完成之后,在最后插入
New->next = Phead->next;
Phead->next = New;
return true;
}
中间插入结点

/***************************************************
* 函数名称: CclLkList_MediumInsertNode
* 函数功能: 在scr_data数据后面插入dest数据
* 函数参数:
* @Head :要插入链表的地址
* @dest_data :源数据,在这个后面插入
* @data :目标数据,需要插入的数据
* 返回结果: true:插入成功。false:插入失败。
* 注意事项: 如果是空链表,不能插入
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_MediumInsertNode(CclLkList_t *Head,DataType_t dest_data,DataType_t data)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//创建新的节点
CclLkList_t *New = CclLkList_NewNode(data);
if(NULL == New){
return false;
}
//头结点只是开头不存放数据,循环的尾结点的next要指向首节点,只有头结点的话他是自己指向自己,所以要特殊处理
//需要先判断链表是否为空(只有头结点)
if(Head == Head->next){
//为空的话另外插入
Head->next = New;
return true;
}
//不为空
//定义一个变量去寻找dest_data
CclLkList_t *Phead = Head->next;
while((Phead->data != dest_data) && (Phead->next != Head->next)){
Phead = Phead->next;
}
//判断是否循环结束还没有找到
if((Phead->next == Head->next) && (Phead->data != dest_data)){
printf("dest_data(%d) is not found!\n",dest_data);
return false;
}
//寻找成功可以开始插入
New->next = Phead->next;
Phead->next = New;
return true;
}
头部删除结点
- 在删除结点时,需要判断链表是否为空

/***************************************************
* 函数名称: CclLkList_IsEmpty
* 函数功能: 判断链表是否为空
* 函数参数:
* @Head :头结点的地址
* 返回结果: true:为空;false:不为空
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_IsEmpty(CclLkList_t *Head)
{
return (Head->next == Head) ? true : false;
}
/***************************************************
* 函数名称: CclLkList_HeadDelNode
* 函数功能: 把链表的头结点删除
* 函数参数:
* @Head :要删除链表的地址
* 返回结果: true:删除成功。false:删除失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_HeadDelNode(CclLkList_t *Head)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//判断链表是否为空
if(CclLkList_IsEmpty(Head)){
printf("circular Linklist is empty!!!\n");
return false;
}
//不为空可以删除
//先找到尾结点
CclLkList_t *Phead = Head->next;
while(Phead->next != Head->next){
Phead = Phead->next;
}
//需要判断单向循环链表是否为一个节点,最后需要让头结点指向自己
if(Phead->next == Phead){
Phead->next = NULL;
free(Phead);
Phead = NULL;
Head->next = Head;
return true;
}
//删除首节点
Phead->next = (Head->next)->next;
(Head->next)->next = NULL;
free(Head->next);
Head->next = Phead->next;
return true;
}
尾部删除结点

/***************************************************
* 函数名称: CclLkList_TailDelNode
* 函数功能: 把链表的尾结点删除
* 函数参数:
* @Head :要删除链表的地址
* 返回结果: true:删除成功。false:删除失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_TailDelNode(CclLkList_t *Head)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//判断链表是否为空
if(CclLkList_IsEmpty(Head)){
printf("circular Linklist is empty!!!\n");
return false;
}
//不为空可以删除
//先找到尾结点,以及尾结点的直接前驱
CclLkList_t *Phead = Head->next;
CclLkList_t *Phead_prev = Head;
while(Phead->next != Head->next){
Phead = Phead->next;
Phead_prev = Phead_prev->next;
}
//需要判断单向循环链表是否为一个节点,最后需要让头结点指向自己
if(Phead->next == Phead){
Phead->next = NULL;
free(Phead);
Phead = NULL;
Head->next = Head;
return true;
}
//寻找到尾之后就可以开始删除
Phead_prev->next = Phead->next;
Phead->next = NULL;
free(Phead);
Phead = NULL;
return true;
}
中间删除结点

/***************************************************
* 函数名称: CclLkList_MediumDelNode
* 函数功能: 把data所在的节点删除
* 函数参数:
* @Head :要删除链表的地址
* @data :在这个数据删除
* 返回结果: true:删除成功。false:删除失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_MediumDelNode(CclLkList_t *Head,DataType_t data)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//判断链表是否为空
if(CclLkList_IsEmpty(Head)){
printf("circular Linklist is empty!!!\n");
return false;
}
//寻找到这个数据所在的节点
CclLkList_t *Phead = Head->next;
CclLkList_t *Phead_prev = Head;
while((Phead->data != data) && (Phead->next != Head->next)){
Phead = Phead->next;
Phead_prev = Phead_prev->next;
}
//判断是不是循环了整个链表都没找到
if((Phead->data != data) && (Phead->next == Head->next)){
printf("data(%d) is not found!\n",data);
return false;
}
//需要判断单向循环链表是否为一个节点,最后需要让头结点指向自己
if(Phead->next == Phead){
Phead->next = NULL;
free(Phead);
Phead = NULL;
Head->next = Head;
return true;
}
//现在到这个数据所在的位置之后,开始删除
Phead_prev->next = Phead->next;
Phead->next = NULL;
free(Phead);
Phead = NULL;
return true;
}
遍历链表
/***************************************************
* 函数名称: CclLkList_TraNode
* 函数功能: 遍历整个链表
* 函数参数:
* @Head :要遍历链表的地址
* 返回结果: true:遍历成功。false:遍历失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_TraNode(CclLkList_t *Head)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//判断链表是否为空
if(Head == Head->next){
printf("Circular Linklist is empty!\n");
return false;
}
//需要把Head备份一下,直接备份到可以输出的节点
CclLkList_t * Phead = Head->next;
int i = 1;
//开始遍历
while(Phead->next != Head->next){
printf("[%d]th node is %d\n",i,Phead->data);
Phead = Phead->next;
i++;
}
printf("[%d]th node is %d\n",i,Phead->data);
return true;
}
修改链表的数据
- 根据所给对数据,把链表内部的数据修改
/***************************************************
* 函数名称: CclLkList_DataRecNode
* 函数功能: 根据数据修改节点
* 函数参数:
* @Head :头结点的地址
* @dest_data :需要修改的数据
* @data :修改的数据
* 返回结果: true:修改成功;false:修改失败
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/29
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_DataRecNode(CclLkList_t * Head,DataType_t dest_data,DataType_t data)
{
//1.先判断链表是否存在
if(NULL == Head){
printf("CclLkList head is not exist!!!\n");
return false;
}
//判断链表是否为空
if(CclLkList_IsEmpty(Head)){
printf("circular Linklist is empty!!!\n");
return false;
}
//备份头结点对地址,以免丢失链表
CclLkList_t *Phead = Head->next;
//寻找到数据节点,并且防止没有这个数据
while((Phead->data != dest_data) && (Phead->next != Head->next)){
Phead = Phead->next;
}
//判断数据是否存在
if((Phead->data != dest_data) && (Phead->next == Head->next)){
printf("dest_data(%d) is not exist!!!\n",dest_data);
return false;
}
Phead->data = data;
return true;
}
删除整个链表
/***************************************************
* 函数名称: CclLkList_Del
* 函数功能: 删除整个链表
* 函数参数:
* @Head :要删除链表的地址
* 返回结果: true:删除成功。false:删除失败。
* 注意事项: Node
* 函数作者:
* 创建日期: 2024/04/23
* 修改历史:
* 函数版本: V1.0
***************************************************/
bool CclLkList_Del(CclLkList_t *Head)
{
//先判断链表是否存在
if(NULL == Head){
printf("Circular Linklist head is not exist!!!\n");
return false;
}
//判断链表是否为空
if(CclLkList_IsEmpty(Head)){
Head->next = NULL;
free(Head);
Head = NULL;
return true;
}
//可以开始删除
//需要定义两个变量来记录他的当前结点的前驱节点
//前驱节点走一个删除一个
CclLkList_t * First = Head->next;
CclLkList_t * Phead = Head->next;
CclLkList_t * Phead_prev = Head;
while(Phead->next != First){
Phead_prev->next = NULL;
free(Phead_prev);
Phead_prev = Phead;
Phead = Phead->next;
}
Phead_prev->next = NULL;
free(Phead_prev);
Phead_prev = NULL;
Phead->next = NULL;
free(Phead);
Phead = NULL;
return true;
}
需要的头文件
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

浙公网安备 33010602011771号