【数据结构】链表:看我如何顺藤摸瓜
- 👑专栏内容:数据结构
- ⛪个人主页:子夜的星的主页
- 💕座右铭:日拱一卒,功不唐捐
文章目录
- 一、前言
- 二、链表
- 1、定义
- 2、单链表
- Ⅰ、新建一个节点
- Ⅱ、内存泄漏
- Ⅲ、插入一个节点
- Ⅳ、销毁所有节点
- Ⅴ、反转一个链表
- 3、双向链表
- 4、循环链表
- Ⅰ、单向循环链表
- Ⅱ、双向循环链表
- Ⅲ、循环链表总结
- Ⅳ、一些OJ题
- ①、环形链表
- ②、快乐数
- 三、总结
- 1、区别
- 2、优点
- 3、缺点
一、前言
前面介绍了线性结构中的顺序表,顺序表的随机访问速度非常快,但是它最大的缺点就是插入和删除的时候要移动大量元素。而链表这一数据结构就能完美的解决这一问题。那么链表是如何解决的呢?
二、链表
1、定义
链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
由于是分散存储,为了能够体现出数据元素之间的逻辑关系,每个数据元素在存储的同时,要配备一个指针,用于指向它的直接后继元素,即每一个数据元素都指向下一个数据元素(最后一个指向NULL(空))。
链表中每个元素本身由两部分组成:
● 数据域:存放数据。
● 指针域:存放指向后继结点的地址;
链表的第一个结点被称为:头节点
正因为如此,我们只需要记住链表中的头节点就行,顺着头节点这个藤,逐渐的可以找到链表中其他所有的节点。
typedef struct LinkNode {int data;//数据域struct LinkNode *next;//指针域
}Node;
2、单链表
单链表,顾名思义。链表指向的只有一个方向。
Ⅰ、新建一个节点
Node *getNewNode(int val) {Node *p = (Node *)malloc(sizeof(Node)); //(1)p->data = val; //(2)p->next = NULL; //(3)return p; //(4)
}
(1)开辟一个空间用来存储新的节点
(2)将新建节点的数据放入数据域中
(3)将新节点的指针域置为 NULL
(4)返回新建的节点
Ⅱ、内存泄漏
在进行插入操作之前,先搞明白一个概念,那就是内存泄露。
内存泄漏:由于疏忽或错误造成程序未能释放已经不再使用的内存。
就拿链表来看,我们知道的只有程序内部的头地址,依据头地址中存储的下一个链表的地址来找到下一个链表,从而,拔出萝卜带出泥,找到所有的链表。但是试想一下,加入我们在操作的过程中不小心把其中一个链表的地址弄丢了呢?
就像这样,因为错误的插入,导致弄丢了②和③的地址,导致②和③这两个链表数据在内存内部中无法被找到,也无法被清理。
Ⅲ、插入一个节点
如下图,我们想在①和②中间插入④,或者这样说,将④插入链表的2号位置,应该怎么操作呢?
首先当然应该让p指针走向2号位置的前一个位置,也就是1号位置处。
然后,很多人就会犯这样的错误
p->next = node; //(1)
node ->next = p->next ; //(2)
这样看似没有问题,但是仔细看一下,在执行完(1)后,p->next
已经不是②的地址了。你再执行(2)其实是将node->next
指向node
自己。
这样就造成了前文说的内存泄漏,也就是说②和③从此就会一直呆在你的内存里面,你调用不了也销毁不了这两个链表。所以,我们插入结点时,一定要注意操作的顺序,要先将结点 node
的 next
指针指向下一个节点地址,再把结点 ① 的 next
指针指向结点node
,这样才不会丢失指针,导致内存泄漏。所以,对于刚刚的插入代码,我们只需要把第 1 行和第 2 行代码的顺序颠倒一下就可以了。
Node *insert(Node *head,int pos ,int val) //(1)
{if(pos ==0) //(2){Node *p = getNewNode (val);p->next = head;return p;}Node *p = head; for(int i =1;i<pos;i++) p = p->next; //(3)Node *node = getNewNode(val); //(4)node ->next = p->next ; //(5)p->next = node; //(6)return head;
}
(1)pos是要插入的位置
(2)pos等于0就是放在头地址处
(3)让p指针走向待插入位置的前一个位置
(4)创建一个要插入的节点
(5)先让新建节点的指针指向待原本插入位置的节点地址
(6)让待前一个位置的指针指向新建要插入的节点
上面的代码自然能够很好的完成插入操作,但是这样代码写起来就会很繁琐,而且也容易出错。如何来更好的解决这个问题呢?仔细看下上面的插入操作,代码较长的原因,主要是怕插入的位置是头位置。所以我们要对这一情况进行特殊处理。但是,如果我们建立一个虚拟的头节点呢?这样不就可以解决找个问题了吗?
Node *insert(Node *head,int pos ,int val)
{Node new_head,*p = &new_head; //(1)Node *node = getNewNode(val); new_head.next = head //(2)for(int i=0;i<pos;i++) p= p->next; //(3)node->next = p->next; //(3)p->next = node;//(3)return new_head.next; //(4)
}
(1)新建一个虚拟的头节点
(2)让虚拟头节点的指针指向真实的头地址
(3)进行插入操作
(4)返回虚拟头节点的指向的地址,也就是真实的头地址
Ⅳ、销毁所有节点
注意:销毁所有的节点不能直接free(head)
,因为你如果直接释放了头节点,那么你根本找不到后面其他节点。
所以,应该新建一个指针,指向要销毁节点的下一个节点,再不断的更新这个指针指向的位置,依次销毁所有节点。切记,万万不可只销毁头节点。
void clear(Node *head) {if (head == NULL) return ;for (Node *p = head, *q; p; p = q) //(1){q = p->next; //(1)free(p); //(2)}return ;
}
(1)循环不断更新p指针指向的节点位置
(2)销毁节点
Ⅴ、反转一个链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
常规解法:
struct ListNode* reverseList(struct ListNode* head) {struct ListNode new_head,*p = head,*q;new_head.next = NULL;while(p){q = p->next;p->next = new_head.next;new_head.next = p;p=q;}return new_head.next;}
递归解法:
struct ListNode* reverseList(struct ListNode* head) {if(head == NULL||head->next == NULL)return head;struct ListNode *tail = head->next;struct ListNode *new_head = reverseList(head->next);head->next = tail->next;tail->next = head ;return new_head;}
3、双向链表
双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。
typedef struct DoubleLinkNode
{int data;//数据域struct DoubleLinkNode *pre; //指向前一个的地址struct DoubleLinkNode *next;//指向后一个的地址
}DulNode;
双向链表的优点是可以找到前驱和后继,可进可退。
但是,这同时也让增加和删除变得复杂,需要多分配一个指针存储空间。
4、循环链表
循环链表是指在链表的基础上,表的最后一个元素指向链表头结点,不再是为空。
那么头指针指向那个节点呢?
在循环链表中,头指针指向的是循环链表的最后一个节点。
因为在循环链表中,最后一个节点即类似于前面提到的虚拟头节点,也是一个真实的节点。
Ⅰ、单向循环链表
从一个结点出发可以找到其他任何一个结点。
Ⅱ、双向循环链表
表头结点的 pre
指向表尾结点;表尾结点的next
指向头结点。
双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
Ⅲ、循环链表总结
Ⅳ、一些OJ题
①、环形链表
力扣-141:环形链表
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true
否则,返回false
使用快慢指针法,分别定义两个指针,从头结点出发,快的指针每次移动两个节点,慢的指针每次移动一个节点,如果 快慢指针指针在途中相遇 ,说明这个链表有环。
bool hasCycle(struct ListNode *head) {struct ListNode *p = head,*q=head;while(q&&q->next){p = p->next;q = q->next->next;if(p==q)return true;}return false;
}
②、快乐数
力扣-202:快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true
;不是,则返回 false
。
可以转化为上面的找环问题。
int getNext(int x) {int d, y = 0;while (x) {d = x % 10;y += d * d;x /= 10;}return y;}bool isHappy(int n) {int p = n, q = n;while (q != 1) {p = getNext(p);q = getNext(getNext(q));if (p == q && p != 1) return false;}return true;}
三、总结
1、区别
不同点 | 顺序表 | 链表 |
---|---|---|
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持:O(1) | 不支持:O(N) |
任意位置插入或者删除元素 | 可能需要搬移元素,效率低O(N) | 只需修改指针指向 |
插入 | 动态顺序表,空间不够时需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
缓存利用率 | 高 | 低 |
2、优点
①.链表是一个动态数据结构,无需给出链表的初始大小。
②.任意位置插入删除时间复杂度为O(1)。
与数组不同,在插入或删除元素后,我们不必移动元素。 在链表中,我们只需要更新节点的下一个指针中存在的地址即可。
③.由于链表的大小可以在运行时增加或减小,因此不会浪费内存。
3、缺点
①.存储密度小,因为每个数据元素,都需要额外存储一个指向下一元素的指针(双链表则需要两个指针)。
②.要访问特定元素,只能从链表头开始,遍历到该元素,时间复杂度为 O ( n) 在特定的数据元素之后插入或删除元素,不涉及到其他元素的移动,因此时间复杂度为 O ( 1) 。 双链表还允许在特定的数据元素之前插入或删除元素。
③.存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素(根据单链表还是双链表有所不同)。
相关文章:
【数据结构】链表:看我如何顺藤摸瓜
👑专栏内容:数据结构⛪个人主页:子夜的星的主页💕座右铭:日拱一卒,功不唐捐 文章目录一、前言二、链表1、定义2、单链表Ⅰ、新建一个节点Ⅱ、内存泄漏Ⅲ、插入一个节点Ⅳ、销毁所有节点Ⅴ、反转一个链表3、…...
linux shell 入门学习笔记18 函数开发
概念 函数就是将你需要执行的shell命令组合起来,组成一个函数体。一个完整的函数包括函数头和函数体,其中函数名就是函数的名字。 优点 将相同的程序,定义,封装为一个函数,能减少程序的代码数量,提高开发…...
如何最巧妙回答HR面试“送命题”:你为什么离开上家公司?
一 HR面试存在“送命题”? 一个资深HR朋友聊到,他最近pass掉一个名校高材生。 其实洽谈过程还比较愉悦,小姑娘名校毕业,落落大方,薪酬要求比较合理,各方面都比较符合,最后就在决定要录用时,HR朋友随口问了句 “你为什么离开上家公司?”,小姑娘也是随口说了句“我不喜…...
注意力机制详解系列(五):分支与时间注意力机制
👨💻作者简介: 大数据专业硕士在读,CSDN人工智能领域博客专家,阿里云专家博主,专注大数据与人工智能知识分享,公众号:GoAI的学习小屋,免费分享书籍、简历、导图等资料&…...
创宇盾重保经验分享,看政府、央企如何防护?
三月重保已经迫近,留给我们的准备时间越来越少,综合近两年三月重保经验及数据总结,知道创宇用实际案例的防护效果说话,深入解析为何创宇盾可以在历次重保中保持“零事故”成绩,受到众多部委、政府、央企/国企客户的青睐…...
软件测试面试汇总
在浏览器中输入 URL,回车后发生了什么? 在浏览器中输入URL并按下回车键后,大致流程如下: 1、浏览器解析 URL,提取出协议(例如HTTP、HTTPS)、主机名和路径等信息。 2、浏览器查找该URL的缓存记录࿰…...
空指针,野指针
空指针在C/C中,空指针(null pointer)是指向内存地址0的指针变量。NULL在C/C中的定义为:#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif #endif从上面的代码定义中,我们可以发现在C…...
Mysql Nested-Loop Join算法和MRR
MySQL8之前仅支持一种join 算法—— nested loop,在 MySQL8 中推出了一种新的算法 hash join,比 nested loop 更加高效。(后面有时间介绍这种join算法) 1、mysql驱动表与被驱动表及join优化 先了解在join连接时哪个表是驱动表&a…...
Spark 广播/累加
Spark 广播/累加广播变量普通变量广播分布式数据集广播克制 Shuffle强制广播配置项Join Hintsbroadcast累加器Spark 提供了两类共享变量:广播变量(Broadcast variables)/累加器(Accumulators) 广播变量 创建广播变量…...
飞天云动,站在下一个商业时代的门口
ChatGPT的爆火让AIGC再度成为热词,随之而来的是对其商业化的畅想——不是ChatGPT自身如何盈利,而是它乃至整个AIGC能给现在的商业环境带来多大改变。 这不由得令人想起另一个同样旨在改变世界的概念,元宇宙。不同的是,元宇宙更侧…...
上海分时电价机制调整对储能项目的影响分析
安科瑞 耿敏花 2022年12月16日,上海市发改委发布《关于进一步完善我市分时电价机制有关事项的通知》(沪发改价管〔2022〕50号)。通知明确上海分时电价机制,一般工商业及其他两部制、大工业两部制用电夏季(7、8、9月)和冬季&#x…...
产品新人如何快速上手工作
三百六十行,行行出产品经理:上至封神的乔布斯,下至卖鸡蛋罐饼的阿姨,他们对如何打造自己的产品都会有一套完整的产品思路,这也是为什么说“人人都是产品经理”。这个看似光鲜的“经理”有时也会被戏称产品汪࿰…...
Linux: ARM GIC仅中断CPU 0问题分析
文章目录1. 前言2. 分析背景3. 问题4. 分析4.1 ARM GIC 中断芯片简介4.1.1 中断类型和分布4.1.2 拓扑结构4.2 问题根因4.2.1 设置GIC SPI中断的CPU亲和性4.2.2 GIC初始化:缺省的CPU亲和性4.2.2.1 boot CPU亲和性初始化流程4.2.2.1 其它非 boot CPU亲和性初始化流程5…...
第20篇:Java运算符全面总结(系列二)
目录 4、逻辑运算符 4.1 逻辑运算符 4.2 代码示例 5、赋值运算符 5.1 赋值运算符...
OpenCV4.x图像处理实例-OpenCV两小时快速入门(基于Python)
OpenCV两小时快速入门(基于Python) 文章目录 OpenCV两小时快速入门(基于Python)1、OpenCV环境安装2、图像读取与显示3、图像像素访问、操作与ROI4、图像缩放5、几何变换5.1 平移5.2 旋转6、基本绘图6.1 绘制直线6.2 绘制圆6.3 绘制矩形6.4 绘制文本7、剪裁图像8、图像平滑与…...
【Git】Mac忽略.DS_Store文件
我们在github上经常看到某些仓库里面包含了.DS_Store文件,或者某些sdk的压缩包里面可以看到,这都是由于随着git的提交把这类文件也提交到仓库,压缩也是一样,压缩这个先留着后面处理。 Mac上的.DS_Store文件 .DS_Store 文件&#…...
12.2 基于Django的服务器信息查看应用(CPU信息)
文章目录CPU信息展示图表展示-视图函数设计图表展示-前端界面设计折线图和饼图展示饼图测试折线图celery和Django配合实现定时任务Windows安装redis根据数据库中的数据绘制CPU折线图CPU信息展示 图表展示-视图函数设计 host/views.py def cpu(request):logical_core_num ps…...
【软件测试】接口测试总结
本文主要分为两个部分: 第一部分:主要从问题出发,引入接口测试的相关内容并与前端测试进行简单对比,总结两者之前的区别与联系。但该部分只交代了怎么做和如何做?并没有解释为什么要做? 第二部分࿱…...
代码随想录算法训练营第52天 || 300.最长递增子序列 || 674. 最长连续递增序列 || 718. 最长重复子数组
代码随想录算法训练营第52天 || 300.最长递增子序列 || 674. 最长连续递增序列 || 718. 最长重复子数组 300.最长递增子序列 题目介绍 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或…...
gitblit 安装使用
1 安装服务端 简而言之:需要安装 java,gitblit, git 三个软件 Windows 10环境使用Gitblit搭建局域网Git服务器 前言 安装Java并配置环境安装gitblit并配置启动gitblit为windows服务使用gitblit创建repository并管理用户 1.1 安装Java并配…...
使用 TensorFlow、Keras-OCR 和 OpenCV 从技术图纸中获取信息
简单介绍输入是技术绘图图像。对象检测模型获取图像后对其进行分类,找到边界框,分配维度,计算属性。示例图像(输入)分类后,找到“IPN”部分。之后,它计算属性,例如惯性矩。它适用于不…...
ESP32设备驱动-GUVA-S12SD紫外线检测传感器驱动
GUVA-S12SD紫外线检测传感器驱动 文章目录 GUVA-S12SD紫外线检测传感器驱动1、GUVA-S12SD介绍2、硬件准备3、软件准备4、驱动实现1、GUVA-S12SD介绍 GUVA-S12SD 紫外线传感器芯片适用于检测太阳光中的紫外线辐射。 它可用于任何需要监控紫外线量的应用,并且可以简单地连接到任…...
WIN7下 program file 权限不足?咋整?!!
在WIN7下对Program Files目录的权限问题 [问题点数:40分,结帖人mysunck] 大部分人说要使用manifest,但是其中一个人说: “安装程序要求管理员很正常,你的程序可以在programfiles,但用户数据不能放那里,因…...
119.(leaflet篇)文字碰撞
听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>...
cuda编程以及GPU基本知识
目录CPU与GPU的基本知识CPU特点GPU特点GPU vs. CPU什么样的问题适合GPU?GPU编程CUDA编程并行计算的整体流程CUDA编程术语:硬件CUDA编程术语:内存模型CUDA编程术语:软件线程块(Thread Block)网格(…...
Python 机器学习/深度学习/算法专栏 - 导读目录
目录 一.简介 二.机器学习 三.深度学习 四.数据结构与算法 五.日常工具 一.简介 Python 机器学习、深度学习、算法主要是博主从研究生到工作期间接触的一些机器学习、深度学习以及一些算法的实现的记录,从早期的 LR、SVM 到后期的 Deep,从学习到工…...
Springboot怎么实现restfult风格Api接口
前言在最近的一次技术评审会议上,听到有同事发言说:“我们的项目采用restful风格的接口设计,开发效率更高,接口扩展性更好...”,当我听到开头第一句,我脑子里就开始冒问号:项目里的接口用到的是…...
Jetpack Compose 深入探索系列六:Compose runtime 高级用例
Compose runtime vs Compose UI 在深入讨论之前,非常重要的一点是要区分 Compose UI 和 Compose runtime。Compose UI 是 Android 的新 UI 工具包,具有 LayoutNodes 的树形结构,它们稍后在画布上绘制其内容。Compose runtime 提供底层机制和…...
23.3.2 Codeforces Round #834 (Div. 3) A~E
FG明天补 A-Yes-Yes? 题面翻译 给定 ttt 个字符串,请判定这些字符串是否分别是 YesYesYesYes…\texttt{YesYesYesYes\dots}YesYesYesYes… 的子串。是则输出 YES,否则输出 NO(YES 和 NO 大小写不定)。 Translated by JYqwq …...
一次失败的面试经历:我只想找个工作,你却用面试题羞辱我!
金三银四近在咫尺,即将又是一波求职月,面对跳槽的高峰期,很多软件测试人员都希望能拿一个满意的高薪offer,但是随着招聘职位的不断增多,面试的难度也随之加大,而面试官更是会择优录取小王最近为面试已经焦头…...
吕梁网站制作吕梁安全/中文域名注册
从一个运行了RTX系统的程序中跳转到另一个带有RTX系统的程序时,程序卡在RTX初始化中,在跳转前关闭滴答定时器中断,跳转正常 http://www.keil.com/support/docs/3925.htm...
上海html5网站建设/苏州网络推广seo服务
作者:范军 (Frank Fan) 新浪微博:frankfan7核心竞争力,说白了就是一种掌握稀缺资源的能力。你拥有的资源,别人不能很轻易的获得。对于IT技术人而言,我们需要对自己所希望获取的稀缺资源有很清楚…...
做视频网站 版权怎么解决/南通企业网站制作
简介 虚树,顾名思义就是不真实的树。 它往往出现在一类树形动态规划问题中。 换句话说,虚树实际就是为了解决一类树形动态规划问题而诞生的! 我们从一道经典的虚树题目入手 [SDOI2011]消耗战 链接:https://www.luogu.org/problemn…...
重庆网站建设子沃科技/760关键词排名查询
Android是当前智能手机操作系统的老大,它之所以发展神速,在很大程度上取决于任何人都可以利用Android的源代码定制完全属于自己的嵌入式系统。这就需要我们队Android系统架构有更深层次的了解。Android系统架构分为4层:Linux内核,…...
自己做网站需要花钱吗/网站关键词排名优化价格
319个团队、480人参赛,第三届华为云VR开发应用大赛盛况空前,而新设立的“人气数字人形象奖”“人气虚拟偶像奖”等,让大赛又一次“破圈”,人气直升。通过大赛,我们看到虚拟现实、数字人、元宇宙等正“脱虚向实”&#…...
网站建设中文百/网站排名优化公司哪家好
又有一段时间没有进行整理和总结输出了,其实最近也没有闲着,也是一直在看书学习状态,看Java并发编程相关的知识,之前买了《Java并发编程的艺术》,去年看了一遍。最近又买了《Java并发编程实战》,两本书都挺…...