2026/7/24 数据结构3 (单向循环链表)

一、单向循环链表
单向循环的链表的使用规则和普通的单向链表没有较大的区别,需要注意:单向循环链表的尾结点的指针域中必须指向链表的首结点的地址。
由于带头结点的单向循环链表更加容易进行管理,所以以带头结点的为例

image
对于单向循环链表,编写如下c语言代码实现对其增删改查等操作并验证

/********************************************************************************************************
 * @File Name		:CircularLinkedList.c
 * @Author			:bbzhu04@gmail.com
 * @Date			:2026/07/23
 * @Function		:构造函数实现链表元素的增删改查等操作
 * @Notes			:为了提高可移植性,所以链表中元素的数据类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
 * 				 	 另外,为了方便管理链表,所以用户设计CircLList_t结构体,该结构体中包含两个成员:结点的数据域和指针域
* Copyright (c)  2025-2026   bbzhu04@gmail.com   All right Reserved
* ******************************************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

//指的是单向循环链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int  DataType_t;

//构造单向循环链表的结点,链表中所有结点的数据类型应该是相同的
typedef struct CircularLinkedList
{
	DataType_t  		 data; //结点的数据域
	struct CircularLinkedList	*next; //结点的指针域 

}CircLList_t;

/********************************************************************************************************
 * @Function Name		:CircLList_Create
 * @Functionality		:创建一个空单向循环链表,空链表应该有一个头结点,对链表进行初始化
 * @Function Parameters	:None
 * @Return result		:指向链表管理结构体的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
CircLList_t * CircLList_Create(void)
{
	//1.创建一个头结点并对头结点申请内存
	CircLList_t *Head = (CircLList_t *)calloc(1,sizeof(CircLList_t));
	if (NULL == Head)//calloc申请内存失败
	{
		perror("Calloc memory for Head is Failed");
		exit(-1);
	}

	//2.对头结点进行初始化,头结点是不存储数据域,指针域指向自身,体现“循环”思想
	Head->next = Head;

	//3.把头结点的地址返回即可
	return Head;
}

/********************************************************************************************************
 * @Function Name		:CircLList_NewNode
 * @Functionality		:创建新的结点,并对新结点进行初始化(数据域 + 指针域)
 * @Function Parameters	:
 *                          @data:要插入到新结点中的数据
 * @Return result		:指向链表新结点的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
CircLList_t * CircLList_NewNode(DataType_t data)
{
	//1.创建一个新结点并对新结点申请内存
	CircLList_t *New = (CircLList_t *)calloc(1,sizeof(CircLList_t));
	if (NULL == New)
	{
		perror("Calloc memory for NewNode is Failed");
		return NULL;
	}

	//2.对新结点的数据域和指针域进行初始化
	New->data = data;
	New->next = NULL;

	return New;
}


/********************************************************************************************************
 * @Function Name		:CircLList_HeadInsert
 * @Functionality		:在单向循环链表的头部插入新的结点
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 *                          @data:要插入到新结点中的数据
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool CircLList_HeadInsert(CircLList_t *Head,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	CircLList_t *New = CircLList_NewNode(data);
	if (NULL == New)
	{
		printf("can not insert new node\n");
		return false;
	}

	//2.判断链表是否为空,如果为空,则直接插入即可
	if (Head->next == Head)
	{
		New->next = Head;
		Head->next = New;
		return true;
	}

	//3.如果链表为非空,则把新结点插入到链表的头部
	New->next  = Head->next;
	Head->next = New;

	return true;
}


/********************************************************************************************************
 * @Function Name		:CircLList_TailInsert
 * @Functionality		:在单向循环链表的尾部插入新的结点
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 *                          @data:要插入到新结点中的数据
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool CircLList_TailInsert(CircLList_t *Head,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	CircLList_t *New = CircLList_NewNode(data);
	if (NULL == New)
	{
		printf("can not insert new node\n");
		return false;
	}

	//2.找到链表的尾结点
	CircLList_t *Phead = Head;//对链表的头结点的地址进行备份
	while (Phead->next != Head)//指针域不为空,则说明当前结点不是尾结点
	{
		Phead = Phead->next;
	}

	//3.将新结点插入到链表的尾部
	Phead->next = New;
	New->next = Head;

	return true;
}



/********************************************************************************************************
 * @Function Name		:CircLList_DestInsert
 * @Functionality		:在循环链表中指定值的后面插入新的结点
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 *                          @destval:目标结点的值
 *                          @data:要插入到新结点中的数据
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/   
bool CircLList_DestInsert(CircLList_t *Head,DataType_t destval,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	CircLList_t *New = CircLList_NewNode(data);
	if (NULL == New)
	{
		printf("can not insert new node\n");
		return false;
	}

	//2.找到目标结点(Phead 指向目标结点本身)
	CircLList_t *Phead = Head->next;  // 从首结点开始找
	while (Phead != Head && Phead->data != destval)
	{
		Phead = Phead->next;
	}

	//3.如果找到了目标结点,在其后面插入新结点
	if (Phead != Head)
	{
		New->next = Phead->next;
		Phead->next = New;
		return true;
	}

	printf("dest node not found\n");
	free(New);
	return false;
}


/********************************************************************************************************
 * @Function Name		:CircLList_HeadDel
 * @Functionality		:删除循环链表首结点
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 * @Return result		:void
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/  
bool CircLList_HeadDel(CircLList_t *Head)
{
	//1.判断链表是否为空,如果为空,则直接返回即可
	if (Head->next == Head)
	{
		printf("list is empty\n");
		return false;
	}

	//2.删除链表的首结点
	CircLList_t *Phead = Head->next;//对链表的首结点的地址进行备份
	Head->next = Phead->next;
	Phead->next = NULL;
	free(Phead);

	return true;
}



/********************************************************************************************************
 * @Function Name		:CircLList_DelNode
 * @Functionality		:删除循环链表中指定的元素(第一个匹配的元素)
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 *                          @data:要删除的元素的值
 * @Return result		:bool型变量,删除成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/  
bool CircLList_DelNode(CircLList_t *Head, DataType_t data)
{
	//1.找到要删除的结点的前驱结点
	CircLList_t *Phead = Head;//对链表的头结点的地址进行备份
	while (Phead->next != Head && Phead->next->data != data)//指针域不为空,则说明当前结点不是尾结点,并且当前结点的直接后继的值不等于要删除的值
	{
		Phead = Phead->next;
	}

	//2.如果找到了要删除的结点,则删除它
	if (Phead->next != Head)//指针域不为空,则说明当前结点不是尾结点,并且当前结点的直接后继的值等于要删除的值
	{
		CircLList_t *Temp = Phead->next;
		Phead->next = Temp->next;
		Temp->next = NULL;
		free(Temp);
		return true;
	}

	printf("node not found\n");
	return false;
}



/********************************************************************************************************
 * @Function Name		:CircLList_Print
 * @Functionality		:遍历输出单向循环链表的元素
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 * @Return result		:void
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/  
bool CircLList_Print(CircLList_t *Head)
{
	//对单向循环链表的头结点的地址进行备份
	CircLList_t *Phead = Head;
	
	//判断当前链表是否为空,为空则直接退出
	if (Head->next == Head)
	{
		printf("current linkeflist is empty!\n");
		return false;
	}

	//从首结点开始遍历
	while(Phead->next)
	{
		//把头结点的直接后继作为新的头结点
		Phead = Phead->next;

		//判断是否到达尾结点,尾结点的next指针是指向首结点的地址
		if (Phead->next == Head->next)
		{
			break;
		}	

		printf("data = %d\n",Phead->data);//输出头结点的直接后继的数据域
	}

	return true;
}





int main(int argc, char const *argv[])
{

	//1.创建一个空的单向循环链表
	CircLList_t *Head = CircLList_Create();

	//2.在单向循环链表的头部插入新的结点
	CircLList_HeadInsert(Head, 10);  //10
	CircLList_HeadInsert(Head, 20);  //20 10
	CircLList_HeadInsert(Head, 30);  //30 20 10
	CircLList_Print(Head);
	printf("\n");

	//3.在单向循环链表的尾部插入新的结点
	CircLList_TailInsert(Head, 40);  //30 20 10 40
	CircLList_TailInsert(Head, 50);  //30 20 10 40 50
	CircLList_Print(Head);	
	printf("\n");

	//4.在单向循环链表中指定值的后面插入新的结点
	CircLList_DestInsert(Head, 20, 25);  //30 20 25 10 40 50
	CircLList_DestInsert(Head, 50, 60);  //30 20 25 10 40 50 60
	CircLList_Print(Head);		
	printf("\n");

	//5.删除单向循环链表的首结点
	CircLList_HeadDel(Head);  //20 25 10 40 50 60
	CircLList_Print(Head);
	printf("\n");

	//6.删除单向循环链表中指定的元素(第一个匹配的元素)
	CircLList_DelNode(Head, 25);  //20 10 40 50 60
	CircLList_DelNode(Head, 60);  //20 10 40 50
	CircLList_Print(Head);	
	printf("\n");
	


	
	return 0;
}

代码运行结果
image

posted @ 2026-07-24 15:38  !城北徐公.!  阅读(13)  评论(0)    收藏  举报