数据结构之带头双向循环链表

哈喽艾维巴蒂

熟悉的数据结构专栏上新啦~

今天给大家伙带来的新品是——

带头双向循环链表

何为带头双向循环链表??

下面就一起来探索吧


目录

概念区

不带头单向不循环链表VS带头双向循环链表

带头双向循环链表结构分析

各接口函数分析

初始化链表接口(ListInit)

尾部插入数据接口(ListPushBack)

 头部插入数据接口(ListPushFront)

 尾部删除数据接口(ListPopBack)

 头部删除数据接口(ListPopFront)

任意位置插入数据接口(ListInsert)

 任意位置删除数据接口(ListErase)

 打印链表数据接口(ListDestroy)

 查找数据接口(ListFind)

销毁链表接口(ListDestroy) 

小结


概念区

在上一篇数据结构的介绍中,咱们初步了解了链表的基本结构及其三大特性,分别是带头问题、单向双向问题、是否循环问题,下面就一起来回顾一下这三大特性吧。

带头问题:链表的遍历需要通过一个指针开始,也可以通过一个不存储有效数据的头结点开始,因为其不存储有效数据,只是一个类似哨兵站岗的功能,故也称哨兵位结点;以上介绍中从一个指针开始遍历的链表就是不带头的链表,而从一个不存储有效数据的头结点开始遍历的链表就是带哨兵位头结点的链表。

单向双向问题:单向代表只有一个指针指向下一个结点,双向代表每一个结点都有两个指针,一个指向前一个结点,另一个指向下一个结点。

循环问题:链表的尾结点指向头结点,使链表成环,为循环链表;

链表的尾结点指向NULL,链表可以正常遍历结束,为不循环链表。

不带头单向不循环链表VS带头双向循环链表

带头双向循环链表是链表中实用性较高的结构,它比不带头单向不循环链表拥有更多的便利,如尾部插入结点、插入第一个结点等方面更具备优势,那我们为啥还需要不带头单向不循环链表呢?原因是不带头单向不循环链表在大多数面试过程中考察,而且其也是构成许多复杂结构的子结构,所以也是我们需要掌握的。

带头双向循环链表结构分析

各接口函数分析

初始化链表接口(ListInit)

函数功能:负责建立哨兵位头结点。

 代码如下:

LTNode* ListInit()
{
	//哨兵位头结点
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));//malloc开辟一个结点
	phead->next = phead;//初始只有一个结点,所以next指针和prev指针都指向自身(循环性)
	phead->prev = phead;
	return phead;
}

尾部插入数据接口(ListPushBack)

函数功能:负责在链表尾部插入数据。

图解分析:

 代码如下:

void ListPushBack(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;//保存当前尾结点
	LTNode* newNode = BuynewNode(x);//申请一个新结点
	//phead       tail   newNode
	tail->next = newNode;
	newNode->prev = tail;
	newNode->next = phead;
	phead->prev = newNode;
}

 头部插入数据接口(ListPushFront)

函数功能:实现从哨兵位头结点后插入数据。

 图解分析:

 代码如下:

void ListPushFront(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* newNode = BuynewNode(x);//申请新结点
	LTNode* next = phead->next;//保存哨兵位头结点的下一个结点,待会将在这个结点前插入新结点

	phead->next = newNode;
	newNode->prev = phead;
	newNode->next = next;
	next->prev = newNode;
}

 尾部删除数据接口(ListPopBack)

函数功能:删除尾部结点。

 图解分析:

 代码如下:

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//防止把哨兵位删除
	LTNode* newtail = phead->prev->prev;//找删除后新的尾结点存放起来,这样子才方便释放当前尾结点
	free(phead->prev);//释放尾结点

	newtail->next = phead;//新的尾结点指向phead
	phead->prev = newtail;//链接新的尾结点
}

 注意:删除结点时应防止把哨兵位头结点删除,可加断言防止错误!

 头部删除数据接口(ListPopFront)

函数功能:删除哨兵位结点的next结点。

 图解分析:

代码如下:

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//防止把哨兵位删除
	LTNode* next1 = phead->next;//保存phead的下一个结点,方便释放
	LTNode* next2 = next1->next;//保存phead的下一个下一个结点
	phead->next = next2;//改变链接关系
	next2->prev = phead;
	free(next1);//释放结点
}

任意位置插入数据接口(ListInsert)

函数功能:在pos结点位置前插入新数据。

 代码如下:

void ListInsert(LTNode* pos, ListDataType x)
{
	assert(pos);
	LTNode* posprev = pos->prev;//保存pos的前一个结点
	LTNode* newNode = BuynewNode(x);//申请新结点
	//插入后三者关系:posprev   newNode   pos
	posprev->next = newNode;//链接posprev和newnode
	newNode->prev = posprev;
	newNode->next = pos;//链接newnode和pos
	pos->prev = newNode;
}

 任意位置删除数据接口(ListErase)

函数功能:删除pos位置的结点。

代码如下:

void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* posprev = pos->prev;//保存pos位置的前一个结点
	LTNode* posnext = pos->next;//保存pos位置的后一个结点
	//此时三者位置关系为:posprev  pos   posnext
	posprev->next = posnext;//链接posprev和posnext,跳过pos结点
	posnext->prev = posprev;
	free(pos);//释放结点
}

 打印链表数据接口(ListDestroy)

代码如下:

void ListPrint(LTNode* phead)
{
	LTNode* cur = phead->next;//负责从哨兵位结点的next开始遍历链表
	while (cur != phead)//循环到哨兵位头结点时结束遍历
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

 查找数据接口(ListFind)

函数功能:查找某个数据是否在链表存储当中,若存在,返回该结点;否则返回NULL。

 代码如下:

LTNode* ListFind(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;//遍历链表
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;//找到返回该结点
		}
		cur = cur->next;
	}
	return NULL;
}

销毁链表接口(ListDestroy) 

代码如下:

void ListDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;//遍历链表
	while (cur != phead)
	{
		LTNode* next = cur->next;//保存下一个结点
		free(cur);//释放当前结点
		cur = NULL;
		cur = next;//迭代走下去
	}
	free(phead);//最后释放哨兵位头结点
}

小结

本次数据结构的冒险到此结束~

感谢各位观众老爷们的观看与指导

如有错误之处还请评论指导小弟一番

下期数据结构:队列与栈再会!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值