【数据结构】拆分详解 - 双向链表

Mid-360激光雷达与IMU标定实战:从环境配置到参数优化 本文详细介绍了Mid-360激光雷达与IMU标定的完整流程,涵盖环境配置、驱动适配、参数优化及实战操作。重点解析了标定参数设置技巧与常见问题解决方案,帮助开发者提升机器人感知系统的定位与建图精度,确保LiDAR-IMU数据融合的最佳效果。 阅读详情


前言

文章细分了各个知识点,可在目录中快速跳转。
手机端用户在查看代码块时建议点击代码右上角放大查看,每一段代码均有完整注释。

画图很重要,学会作图才能理解其中的核心思想,代码的实现反而是其次的,要画图,然后看图写代码。本文在讲解时也遵循先配图,再进行代码实现的先后顺序。


一、单链表与双向链表


在这里插入图片描述

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


二、接口实现 - 带头双向循环链表

  0. 声明

typedef int ListDataType;  //注释1

typedef struct ListNode
{
	ListDataType val;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;  //注释2

双向循环链表的节点内拥有两个指针,一个指向前一个节点,一个指向后一个节点

注释:

  1. 由于后期我们可能会改变链表存储数据的类型,而一但更改,我们需要对每一个调用该类型的地方进行修改,十分麻烦,使用typedef对数据的类型进行重命名,这样以后要更换类型只需要更改此处就可以达到全文替换的目的。

  2. 重命名简便后续输入,注意我们为什么不直接命名简便一点呢?每个命名都是基于英文单词的释义,这样可以增加在多人协作,以及后续维护代码时的可读性。


  1. 创建新节点

LTNode* ListCreate(ListDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->next = NULL;
	newnode->prev = NULL;

	return newnode;
}

  2. 创建并初始化

LTNode* ListInit()
{
	LTNode* phead = ListCreate(-1); //哨兵位的val值随意赋一个即可
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

哨兵位: 我们创建时,首先开辟一个哨兵点,随意赋值即可,因为哨兵位的作用就是使链表始终有一个节点方便指向,不需要它存放数据。


  3. 销毁

void ListDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead;
	LTNode* next = phead->next;

	while (cur->next != phead)
	{
		free(cur);  //free可以置空也可以不置空
		cur = next;
		next = next->next;
	}
}

free后要不要置空:如果指针是在接口函数(局部函数)中创建的,可以不置空,因为函数结束会自动销毁所有局部变量,作为养成一个好习惯,我们可以置空,置空一定没有错。


  4. 打印

void ListPrint(LTNode* phead)
{
	assert(phead);
	printf("哨兵位<=>");

	LTNode* cur = phead->next;
	while (cur != phead) //注释1
	{
		printf("%d<=>", cur->val);
		cur = cur->next;
	}
}

注释:

  1. 也可以写成cur->next != head, 但这样如果我们赋值cur的时候直接赋值成phed(哨兵位)时,这种写法会访问到哨兵位的节点值,有可能乍看一眼发现不了,这是我们不希望的。
    而使用前种写法,我们在打印时就会发现什么都没打印,就会去寻找问题。

  5. 头插

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

  • 方法1
void ListPushFront(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* newnode = ListCreate(x);
	LTNode* first = phead->next;

	newnode->next = first;
	first->prev = newnode;
	newnode->prev = phead;
	phead->next = newnode;
}
  • 方法2
//要考虑链接的先后顺序
void ListPushFront2(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* newnode = ListCreate(x);

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

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

这里我们提供两种写法,对于后面的接口函数也是可以分成两种写法:

  1. 一种是多维护一些指针,这样就不用考虑链接的先后顺序,非常方便。
  2. 一种是不用指针,可以极致节省空间,但需要考虑链接的先后顺序,但对于99%的情况,完全没有必要,只有在嵌入式,单片机这种内存极小的环境下才需要考虑。

  6. 头删

在这里插入图片描述

在这里插入图片描述

void ListPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* first = phead->next;
	LTNode* second = first->next;

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

  7. 尾插

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

void ListPushBack(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* newnode = ListCreate(x);
	LTNode* tail = phead->prev;

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

  8. 尾删

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

void ListPopBack(LTNode* phead)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;

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

	free(tail);
	tail = NULL;
}

  9. 查找

LTNode* ListFind(LTNode* phead, ListDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

  10. pos位置前插入

void ListInsert(LTNode* pos, ListDataType x)
{
	assert(pos);
	LTNode* newnode = ListCreate(x);
	LTNode* posprev = pos->prev;

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

  11.删除pos位置

void ListErase(LTNode* pos)
{
	LTNode* posprev = pos->prev;
	LTNode* posnext = pos->next;

	posprev->next = posnext;
	posnext->prev = posprev;

	free(pos);
	pos = NULL;
}

    1. 利用Insert实现头插

void ListPushFront(LTNode* phead, ListDataType x)
{
	assert(phead);
	//ListInsert(pHead->next, x);
}

    2. 利用Insert实现尾插

void ListPushBack(LTNode* phead, ListDataType x)
{
	assert(phead);
	//ListInsert(pHead, x);
}

    3. 利用Erase实现头删

void ListPopFront(LTNode* phead)
{
	assert(phead);
	//ListErase(pHead->next);
}

    4. 利用Erase实现尾删

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//ListErase(pHead->prev);
}


三、链表与顺序表


  • 链表(双向)

优势:

  1. 任意位置插入删除时间复杂度都是O(1);【已知位置的地址前提下】
  2. 按需申请释放,合理利用空间,不存在浪费

问题:

  1. 下标随机访问不方便,时间复杂度为O(N)
  • 顺序表

优势:

  1. 支持随机的下标访问,复杂度为O(1),适合大规模排序

问题:

  1. 头部或中间删除效率低,需要挪动元素,复杂度为O(N)
  2. 空间不够需要扩容,扩容有一定的消耗,且可能造成空间浪费
  3. 只适合尾插尾删


总结

本文拆分讲解了无头单向不循环链表的接口实现,并配有步骤图解。对比了单双向链表以及链表与顺序表,总结了它们的优缺点。如果对你有所帮助,还望点赞收藏支持博主。
文章中有什么不对的丶可改正的丶可优化的地方,欢迎各位来评论区指点交流,博主看到后会一一回复。

ABAP代码使用OAuth2.0 Client Credential调用外部API ABAP-OAuth2.0-调用外部API 阅读详情

相关推荐

Ubuntu中snap简单介绍

通过Snap可以安装众多的软件包。需要注意的是,snap是一种全新的软件包管理方式,它类似一个容器拥有一个应用程序所有的文件和库,各个应用程序之间完全独立。所以使用snap包的好处就是它解决了应用程序之间的依赖问题,使应用程序之间更容易管理。但是由此带来的问题就是它占用更多的磁盘空间 介绍一些常用命令,有点类似docker 1、查看已经通过snap安装的snap包 snap list可以查看 2、可以搜索想要的snap包,例如tomcat snap search tomcat

icanflying的博客 4万+

链表—分割

链表分割_牛客题霸_牛客网 (nowcoder.com) 思路: 设置两个头,一个用来存放比目标值小的,另外一个用力啊存放比目标值大的,最后链接两个的目标值,记住最后一定要最后一个节点的next设为null public ListNode partition(ListNode pHead, int x) { if(pHead==null)return null; ListNode small=new ListNode(-1); ListNod...

weixin_56401913的博客 174

适合小白的超详细yolov8环境配置+实例运行教程,从零开始教你如何使用yolov8训练自己的数据集(Windows+conda+pycharm)

yolov8源码下载地址:GitHub - ultralytics/ultralytics: NEW - YOLOv8 🚀 in PyTorch > ONNX > OpenVINO > CoreML > TFLiteyolov8使用文档: Home - Ultralytics YOLOv8 Docs教学视频参考: 包会!YOLOv8训练自己的数据集_哔哩哔哩_bilibili先用conda prompt创建一个虚拟环境,我的叫yolov8, python版本用3.10 源码地址:GitHub - ul

CV在读 25万+

面试题——分割链表(算法过程详解)

在原链表上修改:(一)遍历链表,遇到小于x的节点不动跳过,遇到大于等于x的节点尾插到后面,需要创建多个指针,实现逻辑比较麻烦。创建新链表:(二)创建新链表,为避免原链表为空,创建一个空节点,遍历原链表,小于x的节点头插,大于或等于x的节点尾插,头插需要插入到空节点后面(第一个有效节点之前),此过程较麻烦。(三)创建大小两个新链表,分别创建两个空节点为头节点,小于x的节点尾插到小链表,大于或等于x的节点尾插到大链表,然后大小链表首尾相连,此算法思路较清晰,实现也比较容易。

码林的学习分享博客 237

JHU ICBM DTI Atlas纤维束模板介绍

JHU ICBM DTI atlas 纤维束的定位与功能介绍

machinelearning_er的博客 6426

人工智能面试总结-Python

B站:啥都会一点的研究生 公众号试读:啥都会一点的研究生 目录说说Python程序运行过程?说说Python的作用域?说说Python的数据结构?说说Python中可变与不可变类型?说说进程与线程?说说Python中的多线程?说说Python中的多进程?说说Python互斥锁与死锁?说说Lambda?说说Python的深拷贝与浅拷贝?说说Python多线程是否能用多个CPU,为什么?说说Python垃圾回收机制?说说Python里的生成器?说说迭代器与生成器的区别?说说Python 列表的 del,rem.

啥都生的博客 1万+

数据结构——“双向循环链表“ 易懂刨析双向循环链表(图解+代码)

循环链表单向循环链表双向循环链表1. 双向循环链表——插入2. 双向循环链表——删除 单向循环链表 关于两个循环链表合并为一个循环链表 双向循环链表 在单链表L中,查找ai的后继Next(L,a;),耗时仅为O(1),因为取ai后继指针即可。 但查找a;的直接前驱Prior(L,ai);则需从链表的头指针开始,找到结点ai前一结点即是。故运算Prior(L,ai)依赖表长n,耗时为O(n)。另外,若链表中有一指针值被破坏,则整个链表脱节。这是单链表的不足 为此,引入双向链表。先定义双向链表中的结点:

weixin_44278698的博客 2万+

上下双相机定位贴合(原理到应用,halcon源码详解)

上下双相机定位贴合(原理到应用,halcon源码详解) 一、简介(类型与之前的文章)看你们理解那个文章快 主要讲解上下双相机定位贴合的原理和实现过程,包括各种标定、组合使用及具体的halcon源码实现,适用于的X、Y、Z三轴加一旋转轴系统,如模组组成的多轴系统、Scara四轴机器、六轴机器人(在运行过程中保证几个自由度不动,运行即可) 二、标定介绍 这一小节总结了通用的相机和轴的相对位置模型,并对其标定进行详细讲解 本系统仅使用了九点标定,精度可做到0.05mm 九点标定作用: A、本质就是仿射变换,一个坐

苏州程序大白的博客 9974

链表简单分解

/* 链表的简单分解 */ #include &lt;stdio.h&gt; #include &lt;windows.h&gt; #pragma warning(disable:4996) //创建结构体 struct num { char name[10];//记录姓名 int age;//记录年龄 struct num *next;//如果是最后一个结构,它指向NULL...

完美教父的博客 731

利用GAT(图论分析工具箱)构建并分析大脑结构协变网络学习笔记

前言 前面我学习了利用DTI构建白质纤维脑网络,并采用GRETNA计算了小世界网络属性。阅读文献发现可以利用灰质体积或皮层指标(皮层厚度、折叠指数、沟深)等构建结构协变网络再进行网络拓扑属性的计算。因此,我采用前面提取的灰质体积和皮质数据进行了结构协变网络分析,特此写下笔记以便后面的学习。 一、数据准备 二、构建结构协变网络 三、全局属性的统计分析 四、局部属性的统计分析 五、网络攻击分析 六、度的分布 ..................

weixin_52073820的博客 7496

【OpenClaw从入门到精通】第60篇:多智能体协同实战——用“龙虾”搭建你的数字员工团队(2026企业版)

2026年AI Agent企业落地的核心趋势,正从“单Agent单兵作战”转向“多智能体协同组网”。本文以全球开发者先锋大会“百虾大战”真实案例为蓝本,深度拆解OpenClaw多智能体协同的技术内核——三层解耦架构(模型层-技能层-网关层),系统搭建包含项目经理、架构师、执行者、质量把关者的4大核心Agent团队。通过三种主流协作架构对比、全链路配置代码实操、故障自愈实战演练,结合电商新品上架虚拟场景全流程演示,解决企业多Agent部署中的权限隔离、任务协同、运维自治三大核心痛点。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 769

循环链表的三个经典例子

一.约瑟夫问题 据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人,该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从,他将朋友与自己安排在...

一只快乐的野指针D的博客 9082

RNA-seq分析:Step4(比对)

预处理之后的fastq文件,可以使用HISAT2软件将reads比对至参考基因组上。HISAT2软件将reads比对至参考基因组上的步骤如下:1. 对参考基因组进行建索引。对参考基因组进行预处理,生成索引文件,包括基因组序列的FM索引和SA索引。2. 将原始的reads进行预处理。包括去除接头序列、去除低质量的碱基,以及进行过滤,得到质量较高的、可靠的reads。3. 利用建立的FM索引和SA索引,将处理后的reads与参考基因组进行比对。首先,通过FM索引,快速找到与reads匹配的可能位置;

沉香best的博客 2599

【数据结构系列】双向链表

课后习题 上一篇文章中我们说到单链表,然后最后有一道习题,不知道大家有没有做出来,为了照顾一些还不太会的同学,这里专门对这道题进行一个简单的讲解,先来看看原题内容: 有一个带头结点的单链表L = {a1,b1,a2,b2,…,a(n),b(n)},试设计一个算法将其拆分成两个带头结点的单链表L1和L2,L1 = {a1,a2,…,a(n)},L2 = {b(n),b(n - 1),…,b(1)},...

。。。 5789

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

算法例子:拆分(按照奇偶位置)双向链表为两个链表(Java)

1.例子描述 将一个双向链表,按照奇偶位置进行拆分成两个链表 即: 1.双向链表中偶数位置的结点组成一个链表. 2.双向链表中奇数位置的结点组成一个链表. 3.拆分完成后,双向链表变成了空链表. 2.实现思路 4.打印结果 待分割的链表: head->tail:[1,2,3,4,5,6,7,8,9,10] ---------存储偶数位置结点的链表---------- head->tail:[2,4,6,8,10] tail-->head:[10,8,6,4,2] ---------存储奇

forwardyzk的专栏 2489

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

双向链表学习

双链表是一种链式存储结构,每个节点包含数据域和两个指针域(prev指向前驱节点,next指向后继节点),支持双向遍历。其核心操作包括:创建链表、插入(头插、尾插、指定位置前后插入)、删除(指定节点、按位置删除)、查找(按值、按位置、快慢指针找中间节点)等。双链表相比单链表具有双向遍历、插入删除高效(O(1)时间复杂度)等优势,但也存在空间开销大、实现复杂度高、缓存性能较差等缺点。关键操作通过指针调整实现,如插入时修改前后节点的指针关系,删除时断开节点连接并释放内存。

weixin_45854148的博客 1003

c语言分割,C语言拆分双向链表程序

创建一个双向链表,并将此双向链表中的数据项拆分成两个双向链接的示例程序,将以下代码保存到一个源文件中:split_doubly_linked_list.c, 如下所示 -#include #include #include #include struct node {int data;struct node *prev;struct node *next;};struct node *list =...

weixin_36076151的博客 554

数据结构线性表——带头双向循环链表

数据结构、线性表、双链表

2303_78442132的博客 1210

双向链表

双向链表概述 双向链表是一种基本的数据结构,有着广泛的应用。本博文实现了双向链表的创建、删除、遍历、替换、插入(从表头插入和从表尾插入)、元素的移动以及其他的一些链表基本操作(判断是否为空、链表拆分、链表的合并、获取第一个元素、获取下一个元素、获取上一个元素等等)。具体的代码实现可以从http://download.youkuaiyun.com/detail/it_pcode/6632905获取。为了便于学

IT_Linux 1335

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

而双向带头循环链表我们初始化的是哨兵位,本身phead指的就是哨兵位的地址,后续的功能并不需要让phead发生改变,就比如让phead移动到第一个节点的位置,并没有,只需要让phead的next和prev发生改变就行。哨兵位的next要指向新结点,当前第一结点的prev要指向新结点,新节点的prev要指向哨兵位,next要指向当前第一结点。哨兵位的prev指的是尾结点,尾结点的next指的是哨兵位,而新创建的节点的prev要指向原来的尾结点,next要指向哨兵位。2.尾删影响的是哨兵位和尾结点的前驱节点。

2301_80236968的博客 1328

C语言-双向链表

目录 双链表的结构: 创建双向链表: 双向链表查找节点: 双向链表更改节点: 双向链表删除节点: 遍历双链表,并打印元素数据: 双向链表插入节点: 使用双向链表对数据进行“增删查改”的完整操作代码: 双向链表及其创建、双向链表基本操作 可以看出,双向链表中各节点包括: ① 指针域:指向当前节点的直接前驱(前一个)节点; ② 数据域:存储数据元素; ③ 指针域:指向当前节点的直接后继(下一个)节点。 ...

woshizuopie的博客 3263

数据分割C语言程序,C语言拆分双向链表程序

创建一个双向链表,并将此双向链表中的数据项拆分成两个双向链接的示例程序,将以下代码保存到一个源文件中:split_doubly_linked_list.c, 如下所示 –#include #include #include #include struct node { int data; struct node *prev; struct no...

weixin_34580074的博客 389

【数据结构】拆分详解 - 单链表

每一段代码均有完整注释。本文将介绍链表的概念和分类,并重点拆解单链表的实现。

bubble_shadow的博客 723

[数据结构]长整数加法运算(链表)

长整数加法运算(链表) 问题描述 : 假设2个任意长度的整数x、y分别由双向链表A和B存储,现要求设计一个算法,实现x+y。计算结果存储在链表C中。 说明: 由于A和B输出时需要从头至尾遍历,而做加法时需要从尾至头遍历,因此使用双向链表存储。 可以从长整数的低位开始拆分(4位为一组,即不超过9999的非负整数),依次存放在链表的每个结点的数据域中;头结点的数据域存放正负数标志(正数或0:1,负数:-1)。 输入说明 : 第一行:长整数x 第二行:长整数y 输出说明 : 第一行:格式化后的长整数x(从低位到高

codeTcy的博客 6945

java+单向链表+双向链表_java+单向链表+双向链表

java+单向链表+双向链表[2021-02-05 17:16:37]简介:php如何实现链表?2020-07-10php实现链表的方法:首先定义一个节点类,代码为【function __construct($val=null)】;然后实现链表的实现类,代码为【function_construct $this->dummyhead = new Nod】。php实现C语言合并两个有序链表的...

weixin_31125161的博客 324

基于《算法与数据结构》链表的学习实现高精度计算

在大家都以数组实现高精度算法时,链表实现寥寥无几,今天我们聊聊如何用双向循环链表来实现高精度算法

NixLockhart的博客 2349

LinkedList双向链表的详细介绍

在Java源代码中,可以看出其常用的父接口有List接口,而List接口又继承于Collection接口,由此可以推断出:LinkedList类父接口为List,祖宗接口为Collection。5.根据指定索引删除元素public E remove(int index),首先对index索引进行校验,校验通过后调用unlink(Node x)方法,判断要删除的元素所处的位置,进行不同的操作,最后把要删除的元素清空,减小集合大小,增加修改计数器。

qq_68754937的博客 1758
上一篇: 【数据结构】链表OJ - 精选精讲
下一篇: 【数据结构】拆分详解 - 栈
如泡似影
博客等级 码龄3年 812粉丝 27原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值