【数据结构】有头双向循环链表,多图详解,笔记和源码已上传至gitee

有头双向循环链表


🌻🌻笔记和源码自取:
🍄笔记地址
🍄源码地址


最近接触了单链表,也做了很多题目,但单链表的增删查改真的很麻烦

🌸又最近学习了双向链表,尤其是 有哨兵位头节点的双向循环链表

写起来和用起来真的很爽!!🌸

在这里插入图片描述


1. 初始化链表

我们需要一个哨兵位头节点便于尾插头插

在这里插入图片描述

🍀代码如下:

//初始化链表
LTNode* ListInit()
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	phead->next = phead;
	phead->prev = phead;

	return phead;
}


2. 链表尾插

首先创建新的节点

//创建新的节点
LTNode* BuyListNode(LTDateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	assert(newnode);//没有会警告,按道理malloc都是会成功的

	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

🌸插入步骤一:

在这里插入图片描述

🌸步骤二:

在这里插入图片描述

🌸步骤三:

在这里插入图片描述

🌸步骤四:

在这里插入图片描述

🍀代码如下:

//尾插
void ListPushBack(LTNode* phead, LTDateType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;
	
	tail->next = newnode;
	newnode->prev = tail;
	phead->prev = newnode;
	newnode->next = phead;

}

🍀先写一个打印函数用来测试:

//打印链表
void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;

	while (cur != phead)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

🍀测试插口:

//测试尾插
void testList1()
{
	LTNode* plist = NULL;
	plist = ListInit();

	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);

	ListPrint(plist);
}

🍀效果:

在这里插入图片描述


3. 链表头插

newnode插入进去

在这里插入图片描述

🌸步骤一和二:

在这里插入图片描述

🌸步骤三和四:

在这里插入图片描述

🍀代码:

//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* next = phead->next;

	phead->next = newnode;
	newnode->prev = phead;
	next->prev = newnode;
	newnode->next = next;

}

🍀测试用例:

//测试头插
void testList2()
{
	LTNode* plist = NULL;
	plist = ListInit();

	ListPushFront(plist, 4);
	ListPushFront(plist, 3);
	ListPushFront(plist, 2);
	ListPushFront(plist, 1);

	ListPrint(plist);
}

🍀效果:

在这里插入图片描述


4. 链表尾删

将最后一个节点删除

新的尾节点是tail

在这里插入图片描述


🌸第一步:

在这里插入图片描述

🌸第二步:

在这里插入图片描述

🌸第三步:

在这里插入图片描述


🍀代码:

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//不能删掉哨兵位

	LTNode* tail = phead->prev->prev;
	phead->prev = tail;
	free(tail->next);
	tail->next = phead;

}

🍀测试用例:

//测试尾删
void testList3()
{
	LTNode* plist = NULL;
	plist = ListInit();

	ListPushFront(plist, 4);
	ListPushFront(plist, 3);
	ListPushFront(plist, 2);
	ListPushFront(plist, 1);

	//尾删
	ListPopBack(plist);
	ListPopBack(plist);
	ListPopBack(plist);


	ListPrint(plist);
}

🍀效果
在这里插入图片描述


5. 链表头删

删除链表中cur
在这里插入图片描述

🌸第一步:

在这里插入图片描述

🌸第二步:

在这里插入图片描述

🌸第三步:

在这里插入图片描述


🍀代码:

//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	LTNode* cur = phead->next;
	phead->next = cur->next;
	cur->next->prev = phead;
	free(cur);

}

🍀测试用例:

//测试头删
void testList4()
{
	LTNode* plist = NULL;
	plist = ListInit();

	ListPushFront(plist, 4);
	ListPushFront(plist, 3);
	ListPushFront(plist, 2);
	ListPushFront(plist, 1);

	//头删
	ListPopFront(plist);
	ListPopFront(plist);
	ListPopFront(plist);


	ListPrint(plist);
}

🍀结果:

在这里插入图片描述


6. 链表中间插入

在这里插入图片描述

其实这一步和尾插有些类似

🌸步骤图:

在这里插入图片描述

在这里插入图片描述


7. 链表中间删除

在这里插入图片描述
🌸和尾删差不多注意不要删了哨兵

//中间删除
void ListErase(LTNode* pos)
{
	assert(pos);
	assert(pos->next);//防止哨兵被删

	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);

}

🍀测试代码

//测试中间插入 + 中间删除
void testList5()
{
	LTNode* plist = NULL;
	plist = ListInit();

	//插入
	ListPushFront(plist, 4);
	ListPushFront(plist, 3);
	ListPushFront(plist, 2);
	ListPushFront(plist, 1);

	//查找
	LTNode* insert = ListFind(plist, 3);

	//中间插入
	ListInsert(plist->prev, 10);//模拟头插
	ListInsert(plist, 11);//模拟尾插
	ListInsert(insert, 22);//模拟中间

	ListPrint(plist);

	//中间删除
	ListErase(insert->next);//删除22
	ListErase(plist->next);//头删
	ListErase(plist->prev);//尾删

	ListPrint(plist);
}

🍀测试结果:

在这里插入图片描述

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凛音Rinne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值