双向链表的接口设计

对双向链表的增删改查功能的定义

/*******************************************************************
*
*	文件名称  :	02 双向链表的接口程序
*	文件作者  : mailLinL@163.com 
*	创建日期  :  2024/04/23
*	文件功能  :  对双向链表的增删改查功能的定义
* 	注意事项  :  None
*
*	CopyRight (c)  2024   mailLinL@163.com   All Right Reseverd 
*
* *****************************************************************/

定义双向链表中的结点有效数据类型,用户可以根据需要进行修改

typedef int  DataType_t;

构造双向链表的结点,链表中所有结点的数据类型应该是相同的

typedef struct DoubleLinkedList
{
	DataType_t  		     data; //结点的数据域
	struct DoubleLinkedList	*prev; //直接前驱的指针域
	struct DoubleLinkedList	*next; //直接后继的指针域

}DoubleLList_t;

创建双向链表,并创建管理双向链表的头结点

/*******************************************************************
*
*	函数名称:	DoubleLList_Create
*	函数功能:   创建双向链表,并创建管理双向链表的头结点
* 	函数参数:	none
*	返回结果:	管理双向链表的头结点地址
* 	注意事项:   None
* 	函数作者:   mailLinL@163.com
*	创建日期:   2024/04/23
*	修改历史:	
*	函数版本:	V1.0
* *****************************************************************/

//创建一个空双向链表,空链表应该有一个头结点,对链表进行初始化
DoubleLList_t *DoubleLList_Create(void)
{
	// 1.创建一个头结点并对头结点申请内存
	DoubleLList_t *Head = (DoubleLList_t *)calloc(1, sizeof(DoubleLList_t));
	if (NULL == Head)
	{
		perror("Calloc memory for Head is Failed");
		exit(-1);
	}
	// 2.对头结点进行初始化,头结点是不存储数据域,指针域指向NULL
	Head->prev = NULL;
	Head->next = NULL;
	// 3.把头结点的地址返回即可
	return Head;
}

创建新的结点,并对新结点进行初始化(数据域 + 指针域)

/*******************************************************************
*
*	函数名称:	DoubleLList_NewNode
*	函数功能:   创建新的双向链表结点
* 	函数参数:	
*			   @data  : 传入结点的数据域的值
*	返回结果:   新结点的地址
*			   @New  : 返回新结点地址便于操作
* 	注意事项:   None
* 	函数作者:   mailLinL@163.com
*	创建日期:   2024/04/23
*	修改历史:	
*	函数版本:	V1.0
* *****************************************************************/
DoubleLList_t *DoubleLList_NewNode(DataType_t data)
{
	// 1.创建一个新结点并对新结点申请内存
	DoubleLList_t *New = (DoubleLList_t *)calloc(1, sizeof(DoubleLList_t));
	if (NULL == New)
	{
		perror("Calloc memory for NewNode is Failed");
		return NULL;
	}

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

	return New;
}

遍历打印链表

/*******************************************************************
 *
 *	函数名称:	DoubleLList_PrintList
 *	函数功能:   遍历打印链表
 * 	函数参数:
 *			   @data  : 传入结点的数据域的值
 *   返回结果:
 *			   @New  : 返回新结点地址便于操作
 * 	注意事项:   None
 * 	函数作者:   mailLinL@163.com
 *	创建日期:   2024/04/25
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
int DoubleLList_PrintList(DoubleLList_t *Head)
{
	if (NULL == Head->next)
	{
		printf("Linked List is emtoy!");
		return 0;
	}

	while (NULL != Head->next)
	{
		printf("%2d", Head->next->data);
		Head = Head->next;
	}
	printf("\n");
	return 0;
}

向双向链表中的头部插入新结点

/*******************************************************************
*
*	函数名称:	DoubleLList_HeadInsert
*	函数功能:   向双向链表中的头部插入新结点
* 	函数参数:
*			    @Head    :传入需要操作的链表头结点
*			    @data    :传入需要插入的新结点的数据
*	返回结果:   插入是否成功
* 	注意事项:   None
* 	函数作者:   mailLinL@163.com
*	创建日期:   2024/04/23
*	修改历史:
*	函数版本:	V1.0
* *****************************************************************/
bool DoubleLList_HeadInsert(DoubleLList_t *Head, DataType_t data)
{
	// 1.创建新结点,定义指针记录新结点的地址
	DoubleLList_t *New = DoubleLList_NewNode(data);

	// 2.判断新结点是否申请成功
	if (NULL == New)
	{
		return false;
	}

	// 3.当需要操作的链表为空
	if (Head->next == NULL)
	{
		Head->next = New; // 将头结点的next指针指向新结点的地址
		return true;
	}

	// 4.当需要操作的链表非空时
	New->next = Head->next; // 将新结点的next指针指向原首结点的地址
	Head->next->prev = New; // 将原首结点的prev指针指向新结点的地址
	Head->next = New;		// 将头结点的next指针指向新结点的地址

	return true;
}

向双向链表中的尾部插入新结点

/*******************************************************************
 *
 *	函数名称:	DoubleLList_TailInsert
 *	函数功能:   向双向链表中的尾部插入新结点
 * 	函数参数:
 *			    @Head    :传入需要操作的链表头结点
 *			    @data    :传入需要插入的新结点的数据
 *	返回结果:
 * 	注意事项:   None
 * 	函数作者:   mailLinL@163.com
 *	创建日期:   2024/04/23
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
bool DoubleLList_TailInsert(DoubleLList_t *Head, DataType_t data)
{
	// 1.创建新结点,定义指针记录新结点的地址,并备份头结点的地址
	DoubleLList_t *New = DoubleLList_NewNode(data);
	DoubleLList_t *Phead = Head;

	// 2.判断新结点是否申请成功
	if (NULL == New)
	{
		return false;
	}

	// 3.当需要操作的链表为空
	if (Head->next == NULL)
	{
		Head->next = New; // 将头结点的next指针指向新结点的地址
		return true;
	}

	// 4.当需要操作的链表不为空时,遍历链表找到尾结点
	while (Phead->next)
	{
		Phead = Phead->next;
	}
	New->prev = Phead; // 将新结点的prev指针指向尾结点的地址
	Phead->next = New; // 将尾结点的next指针指向新结点的地址
	return true;
}

向双向链表中的目标结点后插入新结点

/*******************************************************************
 *
 *	函数名称:	DoubleLList_DestInsert
 *	函数功能:   向双向链表中的目标结点后插入新结点
 *	函数参数:
 *			    @Head    :传入需要操作的链表头结点
 *			    @data    :传入需要插入的新结点的数据
 *			    @destval :传入目标结点的数据域的值
 *	返回结果:
 *	注意事项:   None
 *	函数作者:   mailLinL@163.com
 *	创建日期:   2024/04/23
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
bool DoubleLList_DestInsert(DoubleLList_t *Head, DataType_t data, DataType_t destval)
{
	// 1.创建新结点,定义指针记录新结点的地址,并备份头结点的地址
	DoubleLList_t *New = DoubleLList_NewNode(data);
	DoubleLList_t *Phead = Head->next;

	// 2.判断新结点是否申请成功
	if (NULL == New)
	{
		return false;
	}

	// 3.当需要操作的链表为空,即没有目标结点,插入失败
	if (Head->next == NULL)
	{
		return false;
	}

	// 4.遍历链表寻找目标结点
	while (Phead->data != destval)
	{
		Phead = Phead->next;
		if (NULL == Phead->next)
		{ // 当遍历到尾结点直接前驱时,直接前驱的next指针指向的地址下的数据与目标数据不同 没有目标结点 退出程序
			return false;
		}
	}

	// 5.在目标结点后插入新结点
	// 当目标结点不是尾结点
	if (Phead->next != NULL)
	{
		Phead->next->prev = New; // 将新结点的地址存入目标结点的下一个结点中指向直接前驱的指针域中
		New->next = Phead->next; // 将目标结点的直接后继的地址存入新结点中指向直接后继的指针域中
		Phead->next = New;		 // 将新结点的地址存入目标结点指向直接后继中指针域中
		New->prev = Phead;		 // 将目标结点的地址存入新结点指向直接前驱的指针域中
	}
	// 如果目标结点是尾结点
	else if (Phead->next == NULL)
	{
		Phead->next = New; // 将新结点的地址存入目标结点的next指针
		New->prev = Phead; // 将目标结点的地址存入新结点的prev指针中
	}

	return true;
}

删除双向链表中的首结点

/*******************************************************************
 *
 *	函数名称:	DoubleLList_HeadDel
 *	函数功能:   删除双向链表中的首结点
 * 	函数参数:
 *			   @Head :传入需要操作的链表的头结点
 *	返回结果:
 *	注意事项:   None
 *	函数作者:   mailLinL@163.com
 *	创建日期:   2024/04/23
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
bool DoubleLList_HeadDel(DoubleLList_t *Head)
{
	// 1.判断链表是否为空
	if (Head->next == NULL)
	{
		printf("Double linkeflist is empty!\n");
		return false;
	}

	// 2.备份首结点的地址
	DoubleLList_t *Phead = Head->next;

	// 3.如果非空,判断链表是否仅有一个结点
	if (NULL == Head->next->next)
	{
		Head->next = NULL; // 将头结点的next指针指向NULL
		free(Phead);	   // 释放首结点的内存
	}

	// 4.如果不是只有一个结点
	else if (NULL != Head->next->next)
	{
		Head->next = Phead->next; // 将头结点的next指针指向首结点直接后继的地址
		Phead->next->prev = NULL; // 将首结点直接后继的prev指针指向NULL
		free(Phead);			  // 释放原首结点的内存
	}
}

删除双向链表中的尾结点

/*******************************************************************
 *
 *	函数名称:	DoubleLList_TailDel
 *	函数功能:   删除双向链表中的尾结点
 * 	函数参数:
 *			   @Head :传入需要操作的链表的头结点
 *	返回结果:
 *	注意事项:   None
 *	函数作者:   mailLinL@163.com
 *	创建日期:   2024/04/23
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
bool DoubleLList_TailDel(DoubleLList_t *Head)
{
	// 1.判断链表是否为空
	if (Head->next == NULL)
	{
		return false;
	}

	// 2.备份首结点的地址
	DoubleLList_t *Phead = Head->next;

	// 3.遍历寻找链表的尾结点
	while (Phead->next != NULL)
	{
		Phead = Phead->next;
	}

	// 4.删除尾结点
	// 当链表中只有一个结点
	if (Phead == Head->next)
	{
		Phead->prev = NULL;
		Head->next = NULL; // 将头结点的next指针指向NULL
		free(Phead);	   // 释放结点内存
	}
	// 当链表不止有一个节点
	else if (Phead != Head->next)
	{
		Phead->prev->next = NULL; // 将尾指针直接前驱的的next指针指向NULL
		Phead->prev = NULL;		  // 将尾指针的prev指针指向NULL
		free(Phead);			  // 释放尾结点内存空间
	}
	return true;
}

删除双向链表中的指定结点

/*******************************************************************
 *
 *	函数名称:	DoubleLList_DestDel
 *	函数功能:   删除双向链表中的指定结点
 * 	函数参数:
 *			   @Head    :传入需要操作的链表的头结点
 *			   @destval :传入与目标结点数据与相同的数值
 *	返回结果:
 *	注意事项:   None
 *	函数作者:   mailLinL@163.com
 *	创建日期:   2024/04/23
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
bool DoubleLList_DestDel(DoubleLList_t *Head, DataType_t destval)
{
	// 1.判断链表是否为空
	if (Head->next == NULL)
	{
		return false;
	}
	// 2.备份首结点的地址
	DoubleLList_t *Phead = Head->next;
	// 3.遍历寻找链表中的目标结点
	while (Phead->data != destval)
	{
		Phead = Phead->next;
		if (NULL == Phead->next)
		{
			return false;
		}
	}
	// 4.删除目标结点
	// 当链表只有一个结点
	if (Phead == Head->next)
	{
		Head->next = NULL; // 将头结点的next指针指向NULL
		free(Phead);	   // 释放结点内存
	}
	// 当链表不止有一个节点
	else if (Phead != Head->next)
	{
		Phead->prev->next = Phead->next; // 将直接前驱的next指针指向直接后继的地址
		Phead->next->prev = Phead->prev; // 将直接后继的prev指针指向直接前驱的地址
		Phead->prev = NULL;				 // 将目标结点的prev指针指向NULL
		Phead->next = NULL;				 // 将目标结点的next指针指向NULL
		free(Phead);					 // 释放目标结点的内存空间
	}
	return true;
}

总结

对链表操作前,需要判断链表是否为空。
对非空链表操作时,需要考虑链表是否只有单独结点,以及特殊节点的操作方式。
一定注意先连接,再断开(双向指针域)。

以下为代码测试结果

image

posted @   林大官人995  阅读(10)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示