【C语言版数据结构】带头双向循环链表接口函数代码

本文详细介绍了C语言中的带头双向循环链表数据结构,包括初始化、打印、新结点操作(尾插、头插)、删除节点以及链表销毁等关键函数的代码实现和使用方法。

hello,我是~小鹿,对你有帮助可以收藏随时找到这里哦

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,另外这个结构虽然结构复杂,但是使用代码实现后会发现结构会带来很多又是,实现反而简单了。实现比无头单向非循环更简单。

带头双向循环链表图示:
注意哨兵位头结点(head)不存储有效数据
在这里插入图片描述
带头双向循环链表的基本结构:

typedef int LTDataType;//类型重命名,方便修改数据类型

typedef struct ListNode
{
	LTDataType data;//结点中存储的数据
	struct ListNode* next;//结点中存储的下一个结点的地址
	struct ListNode* prev;//结点中存储的前一个结点的地址
}LTNode;

带头双向循环链表的实现:

初始化 ListInit

图示:
在这里插入图片描述

代码:

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

	return phead;
}

打印 ListPrint

void ListPrint(LTNode* phead)//打印
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)//因为是循环链表,所以当cur指向phead位置时即打印完毕
	{
		printf("%d -> ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

开辟新结点 BuyListNode

LTNode* BuyListNode(LTDataType x)//开辟新结点
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));

	newnode->data = x;//放入要插入的值
	newnode->next = NULL;
	newnode->prev = NULL;

	return newnode;
}

尾插 ListPushBack

图示:
在这里插入图片描述

代码:

void ListPushBack(LTNode* phead, LTDataType x)//尾插
{
	//哨兵位头结点(head)不存储有效数据,头结点是肯定存在的 
	assert(phead);

	LTNode* tail = phead->prev;//tail是最后一个结点的指针
	LTNode* newnode = BuyListNode(x);//开辟新结点,之后将这个结点放进链表中

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

	//或者使用pos位置之前插入数据函数ListInser也可实现尾插
	//ListInsert(phead, x);
}

尾删 ListPopBack

图示:
在这里插入图片描述
代码:

void ListPopBack(LTNode* phead)//尾删
{
	assert(phead);
	assert(phead->next != phead);//哨兵位头结点是不存储有效数据的,尾删时要保证哨兵位存在,若==则已经表示链表为空

	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;//若不定义倒数第二个指针,尾删再free(tail)后使用的tail为野指针
	free(tail);
	tail = NULL;

	tailprev->next = phead;
	phead->prev = tailprev;

	//或者使用
	//ListErase(phead->prev);
}

头插 ListPushFrond

图示:
在这里插入图片描述

代码:

void ListPushFrond(LTNode* phead, LTDataType x)//头插,插在哨兵位后面
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);//开辟新结点存放x
	LTNode* next = phead->next;

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

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

	//或者使用pos之前位置插入
	//ListInsert(phead->next, x);
}

头删 LTPopFrond

图示:在这里插入图片描述
代码:

void LTPopFrond(LTNode* phead)//头删
{
	assert(phead);
	assert(phead->next != phead);//链表空

	LTNode* next = phead->next;
	LTNode* nextnext = next->next;

	phead->next = nextnext;
	nextnext->prev = phead;

	free(next);
	next = NULL;

	//或者使用
	//ListErase(phead->next);
}

查找 ListFind

LTNode* ListFind(LTNode* phead, LTDataType x)//查找
{
	assert(phead);

	LTNode* cur = phead->next;
	while (phead->next != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

在pos的前面进行插入 ListInsert

图示:
在这里插入图片描述

代码:

void ListInsert(LTNode* pos, LTDataType x)//在pos的前面进行插入
{
	assert(pos);

	LTNode* newnode = BuyListNode(x);
	LTNode* posprev = pos->prev;

	posprev->next = newnode;
	newnode->prev = posprev;

	newnode->next = pos;
	pos->prev = newnode;

}

删除pos位置的结点 ListErase

图示:
在这里插入图片描述
代码:

void ListErase(LTNode* pos)//删除pos位置的结点
{
	assert(pos);

	LTNode* posnext = pos->next;
	LTNode* posprev = pos->prev;

	posprev->next = posnext;
	posnext->prev = posprev;
	free(pos);
	pos = NULL;
}

销毁 ListDestroy

void ListDestroy(LTNode* phead)//销毁
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	phead = NULL;//phead置空不会影响plist。
    //但之前的函数都是一级指针,为保持一致性这里也用一级,所以可以调用完这个函数后置空再free(plist);plist=NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

~小鹿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值