带头双向循环链表增删减补详解(附图)

该文详细介绍了双向链表的各种操作,包括如何创建链表、创建节点、头插法、头删法、尾插法、尾删法、打印链表、查找节点、在特定位置插入节点以及删除节点。此外,还提供了销毁链表的函数,涵盖了链表基本操作的完整流程。
摘要由CSDN通过智能技术生成

一、分类
实际中的链表结构非常多样,以下情况组合起来就有8种链表结构,如下图

在这里插入图片描述
二、存储结构

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;//数据域
	struct ListNode* _next;//后驱指针
	struct ListNode* _prev;//前驱指针
}ListNode;

在这里插入图片描述
三、各个功能的实现
1、创建链表

// 创建返回链表的头结点.
ListNode* ListCreate()
{
	ListNode* pHead = (ListNode*)malloc(sizeof(ListNode));//创建一个ListNode类型的指针,并为这个指针指向的位置开辟一个ListNode大小的空间
	pHead->_next = pHead;//后驱指向头节点自己
	pHead->_prev = pHead;//前驱指向头节点
	return pHead;//返回头指针,头指针指向的空间指是头节点
}

在这里插入图片描述
2、创建节点

//创建节点
ListNode* NodeCreate(LTDataType x)
{
	ListNode* Node = (ListNode*)malloc(sizeof(ListNode));
	Node->_next = NULL;
	Node->_prev = NULL;
	Node->_data = x;
	return Node;
}

在这里插入图片描述
3、头插

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	ListNode* next = pHead->_next;//保存头节点的下一个节点地址
	ListNode* node = NodeCreate(x);//创建节点
	node->_next = pHead->_next;//将插入节点的前后驱指向头节点和头节点的下一个节点
	node->_prev = pHead;
	next->_prev = node;//将之前的首元节点的前驱指向新的首元节点
	if (pHead->_next == pHead)//判断是否为空链表
	{
		pHead->_prev = node;//空链表要把头节点的前驱改变
	}
	pHead->_next = node;//改变头节点的后驱
}

在这里插入图片描述
4、头删

void ListPopFront(ListNode* pHead)
{
	if (pHead->_next == pHead)//判断是否为空链表
	{
		return NULL;
	}
	ListNode* head = pHead->_next;//保存原首元节点
	pHead->_next = head->_next;//将头结点的后驱指向新首元节点
	head->_next->_prev = pHead;//将新首元节点的前驱指向头节点
}

在这里插入图片描述
5、尾插

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	ListNode* node = NodeCreate(x);//创建新节点
	ListNode* prev = pHead->_prev;//保存原来的尾节点
	prev->_next = node;//将原来的尾节点的后驱指向新的尾节点
	node->_next = pHead;//将新尾节点的后驱指向头节点
	node->_prev = prev;//将新尾节点的前驱指向原来的尾节点
	pHead->_prev = node;//将头节点的前驱指向新的尾节点
}

在这里插入图片描述
6、尾删

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
	if (pHead->_next == pHead)//判断是否为空链表
	{
		return NULL;
	}
	ListNode* tail = pHead->_prev;//保存原尾节点
	pHead->_prev = tail->_prev;//将头节点的前驱指向原尾节点的前驱
	tail->_prev->_next = pHead;//将原尾节点的前驱的后驱指向头节点
	free(tail);//释放原尾节点的空间
}

在这里插入图片描述
7、打印

// 双向链表打印
void ListPrint(ListNode* pHead)
{
	if (pHead->_next == pHead)//检查是否为空链表
	{
		return NULL;
	}
	ListNode* cur = pHead->_next;//创建一个移动的指针
	while (cur != pHead)//pHead是固定的,cur不停向后移动,会因为循环而相等
	{
		printf("%d ", cur->_data);
		cur = cur->_next;//将指针不断向后移动
	}
	printf("\n");
}

8、寻找

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	if (pHead->_next == pHead)//判断是否为空链表
	{
		return NULL;
	}
	ListNode* cur = pHead->_next;//创建一个向后循环的指针
	while (cur != pHead)
	{
		if (cur->_data == x)//判断
		{
			printf("got it\n");
			return cur;
		}
		cur = cur->_next;
	}
	printf("no this num\n");
}

9、在pos的前面插入

void ListInsert(ListNode* pos, LTDataType x)
{
	ListNode* prev = pos->_prev;//保存pos前的节点地址
	ListNode* node = NodeCreate(x);//创建新节点
	prev->_next = node;//将prev的后驱指向新节点
	node->_next = pos;//将新节点的后驱指向pos
	node->_prev = prev;//将新节点的前驱指向prev
	pos->_prev = node;//将pos的前指向新节点
}

在这里插入图片描述
10、删除pos位置的节点

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	ListNode* prev = pos->_prev;//保存pos的前驱节点的地址
	ListNode* next = pos->_next;//保存pos的后驱节点的地址
	prev->_next = pos->_next;//将prev的后驱指向pos的后驱
	next->_prev = pos->_prev;//将next的前驱指向pos的前驱
	free(pos);//释放pos
}

在这里插入图片描述
11、销毁链表

// 双向链表销毁
void ListDestory(ListNode* pHead)
{
	ListNode* cur = pHead->_next;//创建一个循环的指针
	while (cur != pHead)//判断结束条件
	{
		ListNode* next = cur->_next;//保存cur的后驱节点地址
		free(cur);//释放cur
		cur = next;//将cur指向后驱节点
	}
	free(pHead);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值