2026/7/25 数据结构5(双向循环链表)

一、双向循环链表的原理与应用
双向循环链表与双向链表的区别:指的是双向循环链表的首结点中的prev指针成员指向链表的尾结点,并且双向循环链表的尾结点里的next指针成员指向链表的首结点,所以双向循环链表也属于环形结构。
image

对于上图所示的双向循环链表,编写如下c语言代码实现对其增删改查等操作并验证

/********************************************************************************************************
 * @File Name		:DoubleCircularLinkedList.c
 * @Author			:bbzhu04@gmail.com
 * @Date			:2026/07/23
 * @Function		:构造函数实现双向循环链表元素的增删改查等操作
 * @Notes			:为了提高可移植性,所以链表中元素的数据类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
 * 				 	 另外,为了方便管理链表,所以用户设计DoubleCirLList_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 DoubleCircularLinkedList
{
	DataType_t  		     data; //结点的数据域
	struct DoubleCircularLinkedList	*prev; //直接前驱的指针域
	struct DoubleCircularLinkedList	*next; //直接后继的指针域

}DoubleCirLList_t;

/********************************************************************************************************
 * @Function Name		:DoubleCirLList_Create
 * @Functionality		:创建一个空双向循环链表,空链表应该有一个头结点,对链表进行初始化
 * @Function Parameters	:None
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
DoubleCirLList_t * DoubleCirLList_Create(void)
{
	//1.创建一个头结点并对头结点申请内存
	DoubleCirLList_t *Head = (DoubleCirLList_t *)calloc(1,sizeof(DoubleCirLList_t));
	if (NULL == Head)
	{
		perror("Calloc memory for Head is Failed");
		exit(-1);
	}

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

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

/********************************************************************************************************
 * @Function Name		:DoubleCirLList_NewNode
 * @Functionality		:创建一个空结点,对结点进行初始化
 * @Function Parameters	:
 * 							@data:要存放到新结点中的数据
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
DoubleCirLList_t * DoubleCirLList_NewNode(DataType_t data)
{
	//1.创建一个新结点并对新结点申请内存
	DoubleCirLList_t *New = (DoubleCirLList_t *)calloc(1,sizeof(DoubleCirLList_t));
	if (NULL == New)
	{
		perror("Calloc memory for NewNode is Failed");
		return NULL;
	}

	//2.对新结点的数据域和指针域(2个)进行初始化,指针域指向结点自身,体现“循环”
	New->data = data; 	//新结点的数据域赋值
	New->prev = New;	//新结点的前驱指针域指向自身
	New->next = New;	//新结点的直接后继指针域指向自身

	return New;
}


/********************************************************************************************************
 * @Function Name		:DoubleCirLList_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 DoubleCirLList_HeadInsert(DoubleCirLList_t *Head,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	DoubleCirLList_t *New = DoubleCirLList_NewNode(data);
	if (NULL == New)
	{
		printf("can not insert new node\n");
		return false;
	}

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

	//3.如果链表为非空,则把新结点插入到链表的头部
	New->next  = Head->next;			//新结点的直接后继指针域指向原首结点
	New->prev  = Head->next->prev;		//新结点的前驱指针域指向原首结点的前驱指针域
	Head->next->prev = New;				//原首结点的前驱指针域指向新结点
	New->prev->next = New;				//原首结点的前驱指针域的直接后继指针域指向新结点
	Head->next = New;					//头结点的直接后继指针域指向新结点

	return true;
}


/********************************************************************************************************
 * @Function Name		:DoubleCirLList_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 DoubleCirLList_TailInsert(DoubleCirLList_t *Head,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	DoubleCirLList_t *New = DoubleCirLList_NewNode(data);
	if (NULL == New)
	{
		printf("can not insert new node\n");
		return false;
	}

	//2.找到链表的尾结点
	DoubleCirLList_t *Phead = Head;//对链表的头结点的地址进行备份
	DoubleCirLList_t *Ptail = Head->next->prev;//对链表的头结点的地址进行备份

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

	return true;
}



/********************************************************************************************************
 * @Function Name		:DoubleCirLList_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 DoubleCirLList_DestInsert(DoubleCirLList_t *Head,DataType_t destval,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	DoubleCirLList_t *New = DoubleCirLList_NewNode(data);
	if (NULL == New) //如果新结点创建失败,则直接返回false
	{
		printf("can not insert new node\n");
		return false;
	}

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

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

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


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

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

	return true;
}


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

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

	return true;
}



/********************************************************************************************************
 * @Function Name		:DoubleCirLList_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 DoubleCirLList_DelNode(DoubleCirLList_t *Head, DataType_t data)
{
	//1.判断链表是否为空,如果为空,则直接返回即可
	if (Head->next == Head)
	{
		printf("list is empty\n");
		return false;
	}

	//2.找到要删除的结点
	DoubleCirLList_t *Phead = Head->next;//对链表的首结点的地址进行备份
	while (Phead->next != Head->next && Phead->data != data)//当前结点不是尾结点,并且当前结点的值不等于要删除的值
	{
		Phead = Phead->next;
	}

	//3.找到结点
	if (Phead->data == data)
	{
			Phead->prev->next = Phead->next;  //删除头结点时此语句无法操作Head。
			Phead->next->prev = Phead->prev;
			//特殊情况:删除的是首结点!头结点Head->next需要更新
			if(Phead == Head->next)
			{
				Head->next = Phead->next;
			}
			Phead->next = NULL;
			Phead->prev = NULL;
		free(Phead);
		return true;
	}

	printf("node  %d not found\n", data);
	return false;
}

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

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

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

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

		
	}

	return true;
}


int main(int argc, char const *argv[])
{
	DoubleCirLList_t *Head = DoubleCirLList_Create();
	//1.验证头部插入结点的正确性
	DoubleCirLList_HeadInsert(Head, 10);
	DoubleCirLList_HeadInsert(Head, 20);
	DoubleCirLList_HeadInsert(Head, 30); //30 20 10
	DoubleCirLList_Print(Head);
	printf("\n");	

	//2.验证尾部插入结点的正确性
	DoubleCirLList_TailInsert(Head, 40);
	DoubleCirLList_TailInsert(Head, 50); //30 20 10 40 50
	DoubleCirLList_Print(Head);
	printf("\n");

	//3.验证在指定结点后插入结点的正确性
	DoubleCirLList_DestInsert(Head, 20, 99); //30 20 99 10 40 50
	DoubleCirLList_DestInsert(Head, 50, 88); //30 20 99 10 40 50 88
	DoubleCirLList_Print(Head);
	printf("\n");

	//4.验证删除首结点的正确性
	DoubleCirLList_HeadDel(Head); //20 99 10 40 50 88
	DoubleCirLList_Print(Head);
	printf("\n");

	//5.验证删除尾结点的正确性
	DoubleCirLList_TailDel(Head); //20 99 10 40 50
	DoubleCirLList_TailDel(Head); //20 99 10 40
	DoubleCirLList_Print(Head);
	printf("\n");
	
	//6.验证删除指定结点的正确性
	DoubleCirLList_DelNode(Head, 10); //20 99 40
	DoubleCirLList_DelNode(Head, 20); //99 40
	DoubleCirLList_DelNode(Head, 40); //99
	DoubleCirLList_Print(Head);
	printf("\n");



	return 0;
}

代码运行结果:

image

posted @ 2026-07-25 17:58  !城北徐公.!  阅读(5)  评论(0)    收藏  举报