最强链表:带头双向循环链表(详解)

学习了普通的链表,我们会发现普通链表的插入和删除有时候也是需要分情况讨论的,并不那么轻松,那是因为单链表的结构所导致的,今天我们来学习一种新的链表结构,它会颠覆我们对于链表的认知,我们会发出感叹:哇!链表原来还可以这么玩。

这种链表就是带头双向循环链表,这里我们要来理解,带头指的是带哨兵位的头结点,这个结点只是用来代表整个链表的头,我们并不在其中存储数据,它起到的就是一个中转站的作用,等会在实现双向带头循环链表的时候我们会深有体会。对于实现循环,我们要在链表的结构体中定义两个指针,一个指针叫前驱指针,一个指针叫后置指针,一个指向结点前面,一个指向后面。

这是双向链表的结构体: 

这个是双向循链表的逻辑结构:

这里我们先来看看尾部插入的逻辑,我们可以通过头结点直接找到尾部结点,我们不需要再从前往后找到尾部结点,但是在这之前,我们应该去创建一个头部结点

 之后我们就可以很方便得使用头结点得前驱指针找到最后一个结点的位置

 对于结点的初始化函数,我们一开始是要让它自己与自己循环,也就是首位相连

我们将plist传入,我们不用它来存储数据,我们只需要随便将一个值存储进去就可以(这里我存储的是-1)BuyLTNode是创建结点的函数,与单链表创建结点的方式类似:

 

下面是头部插入的函数: 

 我们来看单链表和双向循环链表的尾部插入对比

我们会发现新的结构比原先的优秀不少,我们不需要再判定这么多条件

 之后我们再来看头部插入逻辑:我们在头部插入的时候,我们一定要先链接后面,不然我们先链接了前面我们就无法找到后面的结点

这个是头部插入函数: 

 对于遍历链表,并打印它,我们只需要知道停止的条件,而这个停止的条件就是当我们循环到头结点就停止:

 

我们再来看同样是尾部删除,现在的尾部删除比单链表的尾部删除的优势在哪里:

现在的尾部删除我们不需要判断是否为空链表,因为现在的双向循环链表为空的时候也是符合这个条件的。

 下面是查找函数:之前差不多

 对于在pos位置前插入数据函数:

 对于删除pos处元素函数:

 对于销毁链表函数:要注意结束条件还是循环到头结点

以上就是关于带头双向循环链表的内容,这个链表看似结构复杂其实操作简单, 我们只需要熟练使用便可以在短时间内创建处一个结构完整的链表。

下面是完整的代码

void Insert(SL* pos, Datatype x);
void Erease(SL* pos);
SL* BuyNode(Datatype x)
{
	 SL* newnode = (SL*)malloc(sizeof(SL));
	 if (newnode == NULL)
	 {
		 perror("malloc_fail");
		 return;
	 }
	 newnode->data = x;
	 newnode->next = NULL;
	 newnode->prev = NULL;
	 return newnode;
}

SL* InitSl()
{
	SL* phead = BuyNode(-1);//里面放什么值无所谓
	phead->prev = phead;
	phead->next = phead;
	return phead;

}
void PrintSl(SL* pc)
{
	SL* cur = pc->next;
	while (cur != pc)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
void PushBack(SL* phead,Datatype x)
{
	//SL* newnode = BuyNode(x);
	//phead->prev->next = newnode;//这里的phead->prev就相当于是尾结点
	//newnode->prev = phead->prev;
	//phead->prev = newnode;
	//newnode->next = phead;
	Insert(phead, x);//修改后的尾部插入
}
void PushFront(SL* phead, Datatype x)
{
	/*SL* newnode = BuyNode(x);
	SL* first = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	first->prev = newnode;
	newnode->next = first;*/
	Insert(phead->next, x);//修改后的头插
}
bool LTEmpty(SL* phead)
{
	assert(phead);
	return phead->next == phead;
}
void PopBack(SL* phead)
{
	//assert(phead);
	//assert(!LTEmpty(phead));
	//SL* tail = phead->prev;
	//SL* tailPrev = tail->prev;
	//tailPrev->next = phead;
	//phead->prev = tailPrev;
	//free(tail);
	//tail = NULL;
	Erease(phead->prev);//修改后的尾部删除
	
}
SL* Find(SL* phead, Datatype x)
{
	assert(phead);
	SL* cur = phead;
	while (cur->next)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
void Insert(SL* pos, Datatype x)
{
	assert(pos);
	SL* newnode = BuyNode(x);
	SL* prev = pos->prev;
	prev->next = newnode;
	newnode->prev = prev;
	pos->prev = newnode;
	newnode->next = pos;
}
void Erease(SL* pos)
{
	assert(pos);
	SL* prev = pos->prev;
	SL* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
	pos == NULL;
}
void DestroySl(SL* phead)
{
	assert(phead);
	SL* cur = phead->next;
	while (cur != phead)
	{
		cur = cur->next;
		free(cur->prev);
		cur->prev = NULL;
	}
	free(phead);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sugar_goat

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

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

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

打赏作者

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

抵扣说明:

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

余额充值