2026/7/25 数据结构4(双向链表)

一、双向链表
如果想要提高单向链表或者单向循环链表的访问速度,则可以在链表中的结点中再添加一个指针域,让新添加的指针域指向当前结点的直接前驱的地址,也就意味着一个结点中有两个指针域(prev + next),也被称为双向链表(Double Linked List)。 、
image

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

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

}DoubleLList_t;


/********************************************************************************************************
 * @Function Name		:DoubleLList_Create
 * @Functionality		:创建双向链表并对链表进行初始化
 * @Function Parameters	:None
 * @Return result		:指向链表管理结构体的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		: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;
}


/********************************************************************************************************
 * @Function Name		:DoubleLList_NewNode
 * @Functionality		:创建新的结点,并对新结点进行初始化(数据域 + 指针域)
 * @Function Parameters	:
 *                          @data:要插入到新结点中的数据
 * @Return result		:指向双向链表新结点的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		: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;
}

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

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

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

	return true;
}

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

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

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

	return true;
}
  
/********************************************************************************************************
 * @Function Name		:DoubleLList_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 DoubleLList_DestInsert(DoubleLList_t *Head, DataType_t destval, DataType_t data)
{
    // 1. 创建新结点
    DoubleLList_t *New = DoubleLList_NewNode(data);
    if (NULL == New)
    {
        printf("can not insert new node\n");
        return false;
    }

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

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

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

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

	//2.删除链表的首结点
	DoubleLList_t *Phead = Head->next;//对链表的首结点的地址进行备份
	Head->next = Phead->next;         //把首结点的直接后继作为新的首结点
	if (Phead->next)                  //如果首结点的直接后继不为空,则把首结点的直接后继的前驱指针域置为NULL
	{
		Phead->next->prev = NULL;     //把首结点的直接后继的前驱指针域置为NULL
	}
	Phead->next = NULL;               //把首结点的直接后继指针域置为NULL
	Phead->prev = NULL;               //把首结点的直接前驱指针域置为NULL
	free(Phead);

}

/********************************************************************************************************
 * @Function Name		:DoubleLList_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 DoubleLList_DelNode(DoubleLList_t *Head, DataType_t data)
{
	//1.找到要删除的结点的前驱结点
	DoubleLList_t *Phead = Head;//对链表的头结点的地址进行备份
	while (Phead->next && Phead->next->data != data)//指针域不为空,则说明当前结点不是尾结点,并且当前结点的直接后继的值不等于要删除的值
	{
		Phead = Phead->next;
	}

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

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


/********************************************************************************************************
 * @Function Name		:DoubleLList_Print
 * @Functionality		:遍历输出双向链表的元素
 * @Function Parameters	:
 *                          @Head:指向链表头结点的指针
 * @Return result		:void
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/  
void DoubleLList_Print(DoubleLList_t *Head)
{
	//对链表的头文件的地址进行备份
	DoubleLList_t *Phead = Head;
	
	while(Phead->next)//指针域不为空,则说明当前结点不是尾结点
	{
		//把头的直接后继作为新的头结点
		Phead = Phead->next;

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

}




int main(int argc, char const *argv[])
{
	DoubleLList_t* list = DoubleLList_Create();
	//1.验证头部插入新结点
	DoubleLList_HeadInsert(list,20);
	DoubleLList_HeadInsert(list,10); //10 20
	DoubleLList_Print(list);
	printf("\n");

	//2.验证尾部插入结点
	DoubleLList_TailInsert(list,30);
	DoubleLList_Print(list);         //10 20 30
	printf("\n");

	//3.验证指定值后插入
	DoubleLList_DestInsert(list,10,15);
	DoubleLList_DestInsert(list,30,35);
	DoubleLList_Print(list);         //10 15 20 30 35
	printf("\n");

	//4.验证删除首结点
	DoubleLList_HeadDel(list);
	DoubleLList_Print(list);         //15 20 30 35
	printf("\n");

	//5.验证删除指定结点
	DoubleLList_DelNode(list,20);
	DoubleLList_DelNode(list,15);
	DoubleLList_DelNode(list,35);
	DoubleLList_DelNode(list,35);     //验证删除不存在的结点
	DoubleLList_Print(list);         //30
	printf("\n");

	return 0;
}

运行结果:

image

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