一听就很吓人的屑双向带头循环链表的完美实现


开篇前的碎碎念

这部分不重要,着急看正文的读者朋友可忽略:

好久没写博客了,空档期很长,长达20天之久,有种不应该有的高三周末玩一下午手机没有学习的罪恶感,也许未必可能大概率是以下原因导致的。

1.特种兵式旅游

整个五月份笔者走过了20个城市,对郑州,青岛,长沙,日照尤为深刻,这些城市在我的18岁留下不可磨灭的影响,趁年轻,多出去看看,逃离这平凡无趣的生活。
在这里插入图片描述

2.花花世界迷人眼

自从五一回来,不知道为什么,认识了一些热情的女孩子,交了很多朋友,主要的精力花对人际关系的维持,虽说和朋友们聊天,分享快乐是一件无可厚非,没有回报但还是义无反顾会去做的事情,但是我还是需要控制,改进方案如下:
减少盯着手机的时间,拿出统一的时间来看消息,否则一来消息就不能自己地回复,碎片化的娱乐会把我的时间撕成碎片。

3.艾尔登法环是天

真的很好玩,尤其是多次被一个boss打败,然后总结经验总结boss特点以及应对方法然后打过boss,如果你也和我一样喜欢魂游,那么这件事真的————泰裤辣!!!
在这里插入图片描述

分享喜悦

上个月参加博文比赛,笔者运气不错,侥幸拿了两块奖牌,这一切的一切离不开读者朋友的支持,致谢!

在这里插入图片描述


正文开篇

一、双向带头循环链表是什么?

1.双向

后一个节点可以找到前一个结点,这是单链表都梦寐以求的功能,结构如下

typedef int DateType ;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	DateType data;

}LTNode; 

2.带头

指的是该链表含虚拟头结点,即哨兵位,目的是方便处理头指针为空的情况

3.循环

顾名思义,就是尾节点可以找到头节点,头节点还能指向尾节点
在这里插入图片描述

4.功能

还能有什么功能呢,无非就是增删查改
在这里插入图片描述
以下是代码实现

#define _CRT_SECURE_NO_WARNINGS	1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DateType ;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	DateType data;

}LTNode;  
//初始化链表
LTNode* Initlist();
//打印双向带头循环链表
void LTPrintList(LTNode* phead);
//销毁节点
void LTDestory(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, DateType x);
 //头插数据
void LTPushfront(LTNode* plist, DateType x);
//头删
void LTpopfront(LTNode* phead);
//尾删
void LTPopBack(LTNode* phead);
//查找,返回该数据的位置
LTNode* LTFind(LTNode* phead, DateType x);
//精准插入数据,在pos之前插入数据
void LTInsert(LTNode* pos, DateType x);
//删除pos位置的数据
void LTErase(LTNode* pos);

二、基础操作实现

1.初始化

如果只有一个节点,那么它肯定是自己指向自己
在这里插入图片描述
代码实现

LTNode* Initlist()
{
	LTNode* phead = BuyLTNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

2.创建节点

使用malloc函数给节点分配空间,按需分配,返回这段空间的首地址newnode
在这里插入图片描述

代码如下:

LTNode* BuyLTNode(DateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

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

3.尾部插入数据(LTPushBack)

初始化链表后,方可导入数据
依次尾插数据,效果为:
在这里插入图片描述
现尾插数据8
操作为:

  • 定义尾指针,通过头指针phead找到尾节点tail
  • 创建新节点newnode
  • 修改指针域
    新节点与tail节点直接的链接
    在这里插入图片描述

新节点与phead之间的连接
在这里插入图片描述
代码实现:

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

4.尾部删除数据(LTPopBack)

即找到倒数第二的节点ptail
然后将tail中数据和指针域置空
在这里插入图片描述

void LTPopBack(LTNode* plist)
{
	assert(plist);
	LTNode* tail = plist->prev;
	LTNode* realtail = tail->prev;
	realtail->next = NULL;
	free(tail);
}

5.头部插入数据(LTPushfront)

在哨兵位和第二个节点之间插入节点
在这里插入图片描述
首先需要定义cur=phead->next
保存phead指向的节点的位置
其次修改哨兵位和新节点的指针指向
最后修改新节点与cur的指针指向
在这里插入图片描述
代码实现

void LTPushfront(LTNode* plist, DateType x)
{
	assert(plist);
	LTNode* cur = plist->next;
	LTNode* newnode = BuyLTNode(x);
	plist->next = newnode;
	newnode->prev = plist;
	newnode->next = cur;
	cur->prev = newnode;

}

6.头部删除数据(LTpopfront)

修改phead和phead->next->next之间的指针关系
将头节点忽视,对头节点冷暴力,自然而然,头节点被内存回收
类似于人和人之间的关系
死亡并不可怕,最可怕的是被遗忘
(PS:作者你在emo什么???)
在这里插入图片描述
代码实现:

void LTpopfront(LTNode* phead)
{
	LTNode* cur = phead->next;
	LTNode* curnext = cur->next;
	phead ->next=curnext;
	curnext->prev = phead;

}

7.打印链表数据(LTPrintList)

定义结构体指针cur依次访问各节点的数据
用一个while循环控制
当cur=phead循环停止
单步调试跟踪发现循环目的达到
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

void LTPrintList(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	assert(cur);
	printf("【sentinel bit】><");
	while (cur!=phead)
	{
		if (cur == NULL)
		{
			return NULL;
		}
		else {
			printf("【%d】><", cur->data);
			cur = cur->next;
		}
	}
	printf("\n");

}

8.销毁链表(LTDestory)

最关键的细节就是一定一定一定要保存要销毁节点的下一个节点的位置,否则不保存找不到下一个节点,无法进行下一步销毁

void LTDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur!=phead)
	{
		if (cur == NULL)
		{
			break;
		}
 		LTNode* next = cur->next;
			free(cur);
			cur = next;
	}
	free(phead);
}

9.定位数据位置(LTFind)

输入链表中的某个数据,返回该数据的地址pos
实现思路很简单,对链表数据遍历,满足要求就返回当前节点地址

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

三.进阶操作

1.定位插入(LTInsert)

需要和LTFind函数搭配使用
先定位,再在pos之前插入数据
在这里插入图片描述

代码实现

void LTInsert(LTNode* pos, DateType x)
{
	assert(pos);
	LTNode* newnode = BuyLTNode(x);
	LTNode* tmp = pos->prev;

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

2.定位删除(LTErase)

需要和LTFind函数搭配使用
先定位,再删除pos位置数据
原链表数据
在这里插入图片描述
定位删除
在这里插入图片描述

代码实现

void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* posprev = pos->prev;
	LTNode* posnext = pos->next;
	posprev->next = posnext;
	if (posnext == NULL)
	{
		return NULL;
	}
	posnext->prev = posprev;
	free(pos);
}

总结

以上就是该链表的所以功能实现
完整代码见笔者gitee网址
查看完整代码点我
欢迎评论区一起交流,我们一起进步

他眺望着她的火车,她的旅程。他可以望见她的火车,但眺望不到她的旅程。
她发现自己的弱点像雨后春笋,任何一场雨下在任何一个角落,笋尖便会猝不及防地钻出地面,若要长成一棵竹子也好,可惜,弱点的春笋,最终都是被人割去食用的。

——《黄雀记》*苏童

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

江财菊圃路彭家桥C++一霸

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

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

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

打赏作者

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

抵扣说明:

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

余额充值