带头双向循环链表原来这么简单?

news2025/2/27 4:59:18

☃️个人主页:fighting小泽
🌸作者简介:目前正在学习C语言和数据结构
🌼博客专栏:数据结构
🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻

文章目录

  • 前言
  • 一.带头双向循环链表的实现
  • 二.List.h
  • 三.List.c
    • 3.1创建一个新节点
    • 3.2链表的初始化
    • 3.3链表的尾插和头插
    • 3.4链表的打印
    • 3.5链表的尾删和头删
    • 3.6查找某个节点
    • 3.7链表的定向插入和删除
    • 3.8链表的销毁
  • 5.结尾

前言

虽然链表的结构有很多种,但我们实际中最常用的还是无头单向不循环链表和带头双向循环链表。

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

在上一节我们进行了单链表的实现,这次我们就实现一下看似困难,但实现起来却很爽的带头双向循环链表。

一.带头双向循环链表的实现

我们先看一看带头双向循环链表的结构
在这里插入图片描述
我们发现链表的每一个节点都存在两个指针,一个指向它的下一个节点,一个指向它的上一个节点。而头节点的前一个指针指向了最后一个节点,最后一个节点的指针指向了头节点,构成了循环结构。当我们想进行找尾操作的时候就不用遍历链表了,是不是很神奇。

接下来我们就实现一下带头双向循环链表。

二.List.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int LTDataType;

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

bool LTEmpty(ListNode* phead);

void LTInit(ListNode** pphead);

void LTPushBack(ListNode* phead, LTDataType x);

void LTPopBack(ListNode* phead);

void LTPushFront(ListNode* phead, LTDataType x);

void LTPopFront(ListNode* phead);

void LTPrint(ListNode* phead);

ListNode* LTFind(ListNode* phead, LTDataType x);

void LTInsert(ListNode* pos, LTDataType x);

void LTErase(ListNode* pos);

void LTDestroy(ListNode* phead);

三.List.c

3.1创建一个新节点

由于我们进行链表的尾插头插时需要创建新的节点,并给它赋一个想要的值。所以我们可以写一个函数来创建新节点,然后返回这个节点的地址就可以了。

ListNode* BuyListNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev= NULL;
	return newnode;
}

3.2链表的初始化

开始我们会创建一个NULL指针,并把它初始化为头节点。由于它是一个循环链表,所以头节点的两个指针都指向它自己。又因为我们想要修改的是一个指针,所以我们需要传头节点的地址,即2级指针。

void LTInit(ListNode** pphead)
{
	*pphead = BuyListNode(-1);
	(*pphead)->next = *pphead;
	(*pphead)->prev = *pphead;
}

3.3链表的尾插和头插

要进行尾插操作,只需要创建一个新节点,通过头节点找到尾节点,将新节点插在这两个节点之间就可以了。

void LTPushBack(ListNode* phead, LTDataType x)
{
	ListNode* newnode = BuyListNode(x);
	phead->prev->next = newnode;
	newnode->prev = phead->prev;
	newnode->next = phead;
	phead->prev = newnode;
}

进行头插操作,也是创建一个新节点,通过头节点找到它的下一个节点,把新节点插在这两个节点之间。

void LTPushFront(ListNode* phead, LTDataType x)
{
	ListNode* newnode = BuyListNode(x);
	ListNode* next = phead->next;
	newnode->next = next;
	newnode->prev = phead;
	phead->next = newnode;
}

3.4链表的打印

  • 单链表进行打印的时候是不是遍历一遍链表,打印节点的每一个值,直到遇到NULL指针停止?但是我们的带头双向循环链表是一直循环的,没有NULL指针。那我们该怎么办呢?
  • 其实也很简单,我们要定义一个cur为当前的节点,从链表的第一个节点开始遍历,每次打印节点的数据并将cur指向下一个节点,如果cur 等于头节点,则停止打印。
void LTPrint(ListNode* phead)
{
	printf("guard<=>");
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

3.5链表的尾删和头删

由于链表只有一个头节点的时候是不能删除的,所以我们可以写一个函数来判断一下

bool LTEmpty(ListNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

尾删我们只需要通过头节点找到尾节点和尾节点的前一个节点,将头节点和尾节点的前一个节点链接起来,再把尾节点释放掉就可以了。

void LTPopBack(ListNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));

	ListNode* tail = phead->prev;
	ListNode* tailprev = tail->prev;

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

	free(tail);
}

头删也是一样的道理,将头节点和头节点下一个节点的下一个节点链接起来,再把头节点的下一个释放掉就可以了。

void LTPopFront(ListNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));

	ListNode* first = phead->next;
	ListNode* second = first->next;

	phead->next = second;
	second->prev = phead;
	free(first);
}

3.6查找某个节点

  • 我们也是通过遍历链表来查找节点,如果节点存在就返回节点的地址,不存在就返回NULL指针。
  • 通过找到某个节点的地址,我们可以直接对它进行插入,删除操作。
ListNode* LTFind(ListNode* phead, LTDataType x)
{
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

3.7链表的定向插入和删除

通过找到的节点地址,直接在它前面插入一个节点即可。

void LTInsert(ListNode* pos, LTDataType x)
{
	ListNode* newnode = BuyListNode(x);
	ListNode* prev = pos->prev;
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

通过找到的节点地址,将它的前一个节点和后一个节点链接起来,然后再把该节点释放掉即可

void LTErase(ListNode* pos)
{
	ListNode* prev = pos->prev;
	ListNode* next = pos->next;
	prev->next = next;
	next->prev = prev;

	free(pos);
}

3.8链表的销毁

void LTDestroy(ListNode* phead)
{
	ListNode* cur = phead->next;
	ListNode* next = NULL;
	while (cur != phead)
	{
		next = cur->next;
		free(cur);

		cur = next;
	}
	free(phead);
}

5.结尾

这些就是我给大家分享的关于带头双向循环链表的知识啦,是不是很简单啊。希望我们都能有所收获!
先赞后看,养成习惯!!^ _ ^
码字不易,大家的支持就是我坚持下去的动力,点赞后不要忘了关注我哦!

如有错误,还请您批评改正(。ì _ í。)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/520610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win/mac电脑最好用的录屏软件Camtasia 2023官方中文版

Camtasia 2023专业的屏幕录制和视频剪辑软件 3000多万专业人士在全球范围内使用Camtasia展示产品&#xff0c;教授课程&#xff0c;培训他人&#xff0c;以更快的速度和更吸引人的方式进行沟通和屏幕分享。使您在Windows和Mac上进行录屏和剪辑创作专业外观的视频变得更为简单。…

Kali-linux使用Maltego收集信息

Maltego是一个开源的漏洞评估工具&#xff0c;它主要用于论证一个网络内单点故障的复杂性和严重性。该工具能够聚集来自内部和外部资源的信息&#xff0c;并且提供一个清晰的漏洞分析界面。本节将使用Kali Linux操作系统中的Maltego&#xff0c;演示该工具如何帮助用户收集信息…

【LeetCode】312. 戳气球

312. 戳气球&#xff08;困难&#xff09; 解法一&#xff1a;动态规划 首先看一个区间&#xff1a; 区间(i,j) 是一个开区间&#xff0c;因为我们只能戳爆 i 和 j 之间的气球&#xff0c;不能戳爆索引为 i 和 j 的气球。 我们不妨考虑该区间内被戳爆的最后一个气球&#xff…

UNIAPP框架中获取当前定位信息

概述 准备生成自己的Android证书。高德地图生成自己的key并配置mainfest.json。设置左上角图标并完成配置。页面中获取经纬度坐标。依据经纬度坐标获取位置信息。更新图标信息完成展示。 第一步&#xff1a;生成Android证书 参考资料 Android平台云端打包证书使用说明 https…

java类和对象之认识1

文章目录 一、Java类二、Java成员变量三、Java方法四、Java类的构造方法五、Java创建对象六、Java使用对象七、Java对象的引用和实体八、Java类与程序的基本结构九、Java参数传值总结 一、Java类 类是组成Java程序的基本要素&#xff0c;一个Java应用程序就是由若干个类所构成…

永磁同步电机(PMSM)无传感器控制基于龙伯格观测器Matlab/Simulink仿真分析

文章目录 前言一、龙伯格观测器1.1.龙伯格观测器的原理2.2.龙伯格观测器的误差2.3.PMSM龙伯格观测器的建立 二、Matlab/Simulink仿真分析2.1.仿真电路分析2.1.1.电机控制模式切换10ms任务2.1.2.速度环控制2ms任务2.1.3. 电流环控制50us任务2.1.4.电机主电路 2.2.仿真结果分析 总…

单调栈模板总结及应用

文章和代码已经归档至【Github仓库&#xff1a;https://github.com/timerring/algorithms-notes 】或者公众号【AIShareLab】回复 算法笔记 也可获取。 文章目录 单调栈模板栈算法模板例题&#xff1a;单调栈基本思路code 单调栈模板 栈&#xff1a;先进后出。 队列&#xff…

shell数组(包含排序算法)

目录 一&#xff1a;数组定义方法 1、方法一 2、方法二 ​3、方法三 ​4、方法四 5、判断数组是否完整 &#xff08;1&#xff09;方法一 &#xff08;2&#xff09;方法二&#xff1a;通过脚本 二&#xff1a;获取数组值 1、获取数组长度 2、获取数组数据列表 3、获…

ipad专用笔和其他笔有什么区别?第三方电容笔了解

要是ipad仅仅用来玩游戏&#xff0c;看电视的话&#xff0c;也太浪费ipad平板的作用了。ipad这个产品&#xff0c;用途还是很广的&#xff0c;既能用于职业绘画&#xff0c;又能用于学习笔记。许多人都觉得苹果的电容笔非常好&#xff0c;但与普通的电容笔相比&#xff0c;它的…

Python常用的开发工具合集

​ Python是一种功能强大且易于学习的编程语言&#xff0c;被广泛应用于数据科学、机器学习、Web开发等领域。随着Python在各个领域的应用越来越广泛&#xff0c;越来越多的Python开发工具也涌现出来。但是&#xff0c;对于新手来说&#xff0c;选择一款合适的Python开发工具可…

美本统计学基础笔记

美本统计学基础笔记 1.基础2.概率3.离散概率分布Discrete Probability Distributions4.The Normal Probability Distribution正态概率分布5.Sampling Distributions采样分布6.Large-Sample Estimation大样本估计7.Large-Sample Tests of Hypotheses假设的大样本检验 1.基础 左…

颜值打分代码实例讲解(paddle框架)

数据集介绍 训练数据集为华南理工大学实验室公布的数据集 数据中包含500张女生图片&#xff0c;分别由70人进行打分&#xff0c;最终取平均值即为该图片的打分情况。 我们在实践中将图片分值设定为1-5。 500张图片中&#xff0c;450张用于训练&#xff0c;50张用于验证。 任…

快速落地基于“AIGC+数字人”的数字化内容生产

谁不想有一个可爱的数字人形象呢&#xff1f;在日常的工作和娱乐中&#xff0c;越来越多的数字人虚拟形象与大家见面&#xff0c;他们可以是主播&#xff0c;也可以是语音助手&#xff0c;还可以是你自己的虚拟宠物。只有更快更精准的生成数字人&#xff0c;才能让数字人更加普…

【JS】1680- 重学 JavaScript API - Beacon API

❝ 前期回顾&#xff1a;1.Page Visibility API 2.Broadcast Channel API ❞ 1. 什么是 Beacon API 1.1 概念介绍 Beacon API 是 HTML5 提供的一种新的浏览器 API&#xff0c;可以用于在浏览器后台异步地发送数据&#xff0c;而不影响当前页面的加载和性能。通过 Beacon API&am…

FE_Vue学习笔记 常用指令的学习【v-model filters v-text v-html v-cloak v-once v-pre 自定义指令】

1 收集表单数据 v-model 收集表单数据&#xff1a; 若&#xff1a;<input type"text">&#xff0c;则v-model收集的是value的值&#xff0c;用户输入的就是value值。 若&#xff1a;<input type"radio">&#xff0c;则v-modle收集的是value的…

Eclipse配置tomcat服务器

1.首先下载tomcat&#xff0c;下载地址&#xff1a;http://maven.apache.org/&#xff0c;下载好后解压至本地磁盘根目录&#xff0c;我是解压至D盘根目录 2.打开Eclipse&#xff0c;进入Window->Preferences 3.找到Server->Runtime Environments 4.再右边点击Add添加一个…

【手撕代码】HDB3编解码

【手撕代码】HDB3编解码 1. 来源和需求 HDB3编解码任务来源于2023年3月4日“FPGA技术讨论群”的一次活动《101群第一次FPGA编码交流研讨会》&#xff0c;要求设计HDB3编解码&#xff0c;本篇文章作者【roy2022】&#xff0c;经作者授权后转发&#xff0c;以下所有文件版权归作者…

软考-高级系统架构师经验分享

【摘要】 2022年7月17从女朋友嘴里了解到有软考这个东西,7月20——7月23日,上班空闲时间百度详细了解了软考的内容、大纲、通过之后的收益,于是决定备考高级架构师考试并上网收集了所有能收集的资料(不论好坏,完成收集后再筛选);经过3个月的复习,2022年11月5日,第一次…

Extra Finance 主网测试版上线,完成任务领空投

DeFi 的广泛应用将上一轮牛市推向顶峰&#xff0c;也让区块链具有了更多的拓展性。经过熊市的洗礼&#xff0c;DeFi 应用开始升级和优化&#xff0c;并且衍生出更多更加具有实用性和创新性的新产品。DeFi 已经成为区块链的基础设施&#xff0c;为更多的应用和创新提供帮助。下一…

ENVI为不含地理参考信息的栅格影像手动添加地理、投影坐标系

本文介绍基于ENVI软件&#xff0c;对不含有任何地理参考信息的栅格遥感影像添加地理坐标系或投影坐标系等地理参考信息的方法。 我们先来看一下本文需要实现的需求。现有以下两景遥感影像&#xff0c;其位于不同的空间位置&#xff1b;但由于二者均不含任何地理参考信息&#…