最复杂链表---带头双向循环链表

文章详细介绍了如何使用C语言定义一个带前驱指针的循环链表结构,并提供了初始化、头插、尾插、头删、尾删、任意位置插入和删除等操作的实现代码。这些操作主要依赖于对指针的熟练操作,通过调整节点间的指针关系来完成各种功能。循环链表的设计使得在链表首尾进行插入和删除操作更为简便。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、结构体定义

typedef int LTDataType;

typedef struct ListNode {
	LTDataType data;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;

 与单链表相比,多了一个前驱指针,看似结构更加复杂,但实际的操作却更加简单

 二、增删查改

1.链表初始化

//创建节点
LTNode* BuyLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;
	}
	newnode->data = x;
	newnode->next = newnode->prev = NULL;
	return newnode;
}

//初始化
LTNode* InitList()
{
	LTNode* phead = BuyLTNode(-1);//头节点里面存啥不重要
    //因为是循环链表,所以phead的前后指针都指向自己
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

2.头插+尾插

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = BuyLTNode(x);
	LTNode* first = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;
}

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyLTNode(x);
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

 3.头删+尾删

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));//链表不能为空,LTEmpty是自定义函数
	LTNode* first = phead->next;
	LTNode* second = first->next;
	phead->next = second;
	second->prev = phead;
	free(first);
}

 

//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));//链表不能为空,LTEmpty是自定义函数
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = phead;
	phead->prev = tailPrev;
}

 4.任意位置的插入删除

//插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* newnode = BuyLTNode(x);
	//posPrev newnode pos
	newnode->prev = posPrev;
	posPrev->next = newnode;
	newnode->next = pos;
	pos->prev = newnode;
}

//删除
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* posNext = pos->next;
	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
}

5.查询和销毁链表

//查找
LTNode* Find(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

//销毁
void LTDestroy(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
//链表置空要在外面指空,与free函数类似

 写完上面的函数,其实你会发现函数的逻辑就是指针之间的相互赋值问题,只要将逻辑图画出来,实际的实现其实并不会复杂,而这都是我们通过指针找到节点的前驱和后继的结构优势导致的,当然画图解决逻辑问题这也是很关键的,希望读者可以好好体会一下上面说的两点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值