双向链表————查找、删除、插入结点 (基于C语言实现)

学校双向链表,并添加遍历、插入、删除结点等函数接口

注意注意的点:

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

下面是我的函数接口

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

typedef int  DataType_t;

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

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

}DoubleLList_t;

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

/******************************************************
 *
 *  name      : DoubleLList_Create
 *	function  : 创建一个空双向链表,空链表应该有一个头结点,对链表进行初始化
 *  argument  : None
 *  retval    : 管理双向链表的头结点地址
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
//创建一个空双向链表,空链表应该有一个头结点,对链表进行初始化
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;
}

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

/******************************************************
 *
 *  name      : DoubleLList_NewNode
 *	function  : 创建新的结点,并对新结点进行初始化(数据域 + 指针域)
 *  argument  
 *              @data :结点的数据
 *              
 *  retval    : 新结点的地址
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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;
}

遍历打印链表

/******************************************************
 *
 *  name      : DoubleLList_PrintList
 *	function  : 遍历打印链表)
 *  argument  
 *              @Head :头结点的地址
 *              
 *  retval    : None
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
void DoubleLList_PrintList(DoubleLList_t *Head)
{
	if (NULL == Head->next)
	{
		printf("Linked List is emtoy!");
		return ;
	}

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

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

/******************************************************
 *
 *  name      : DoubleLList_HeadInsert
 *	function  : 向双向链表中的头部插入新结点
 *  argument  
 *              @Head :头结点的地址
 *              @data : 结点的数据
 *              
 *  retval    : 成功为1,否则为0
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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;
}

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

/******************************************************
 *
 *  name      : DoubleLList_TailInsert
 *	function  : 向双向链表中的尾部插入新结点
 *  argument  
 *              @Head :头结点的地址
 *              @data : 结点的数据
 *              
 *  retval    : 成功为1,否则为0
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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;
}

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

/******************************************************
 *
 *  name      : DoubleLList_DestInsert
 *	function  : 向双向链表中的尾部插入新结点
 *  argument  
 *              @Head :头结点的地址
 *              @data : 结点的数据
 *              
 *  retval    : 成功为1,否则为0
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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;
}

删除双向链表中的首结点

/******************************************************
 *
 *  name      : DoubleLList_HeadDel
 *	function  : 向双向链表中的尾部插入新结点
 *  argument  
 *              @Head :头结点的地址
 *              
 *  retval    : 成功为1,否则为0
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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);			  // 释放原首结点的内存
	}
}

删除双向链表中的尾结点

/******************************************************
 *
 *  name      : DoubleLList_TailDel
 *	function  : 向双向链表中的尾部插入新结点
 *  argument  
 *              @Head :头结点的地址
 *              
 *  retval    : 成功为1,否则为0
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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;
}

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

/******************************************************
 *
 *  name      : DoubleLList_TailDel
 *	function  : 向双向链表中的尾部插入新结点
 *  argument  
 *              @Head :头结点的地址
 *              @destval :传入与目标结点数据与相同的数值
 *              
 *  retval    : 成功为1,否则为0
 *  author    : Dazz
 *  date      : 2024/4/25
 *  note      : None
 *
 * *******************************************************/
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;
}

posted @ 2024-04-24 00:03  Dazz_24  阅读(238)  评论(0)    收藏  举报