【追求卓越02】数据结构--链表
引导
今天我们进入链表的学习,我相信大家对链表都很熟悉。链表和数组一样,作为最基础的数据结构。在我们的工作中常常会使用到。但是我们真的了解到数组和链表的区别吗?什么时候使用数组,什么时候使用链表,能够正确的选取吗?
链表需要我们进行一些练习才能更好的掌握。我后面也会结合几个经典的练习题进行训练。
链表和数组区别
数组和链表的最大区别就是存储了。从上一栏,我们了解到数组的存储结构是连续的。链表与之恰恰相反,它可以利用内存中零碎的内存空间。如图:

这样就存在一个问题;当我们向内存申请100M的数组时,即使内存剩余200M内存,但经常会提示申请失败“out of memory”。因为内存中存在着很多的内存碎片,导致200M内存中基本不存在100M连续的。但是链表就不会存在这种情况,因为它支持动态分配,不需要内存地址上的连续要求。
单链表
单链表,是比较简单的了,并且也比较常见。结构图如下:

单链表的删除,新增操作的时间复杂度?
我们知道数组的删除,新增操作,要进行数据的搬移(保证数据的连续性)。导致数组的复杂度为O(n)。但是单链表并不需要进行数据的搬移,只要修改节点的指针的指向即可。所以链表的复杂度时O(1)。
单链表的随机访问的时间复杂度?
数组的存储是连续的,当知道下标时,数组复杂度O(1);但是链表由于不是连续存储的,所以在访问第i个节点时,需要从头节点,一个接一个遍历。因此链表复杂度O(n)。
循环链表
循环链表其实就是特殊的单链表(尾节点指向了头节点)。因此它也不是很难。不过对于特殊的问题,使用循环链表比较方便。比图经典的约瑟夫问题。结构图如下:

双向链表
双向链表虽然我们接触的不多,但在项目中,双向链表比单链表使用的更加广泛。 双向链表其实就是多了一个指针变量,指向了前节点。结构图如下:

正如我们上面说的双向链表在工作中使用的往往比单链表要广泛,为什么呢?
从链表的删除举例,删除链表的中节点,无非就是以下两种情况:
- 删除节点中,值等于某个特定值的节点
- 删除给定指针指向的节点
对于第一种情况,无论是单链表或双链表都要先找到对应的节点,再进行删除操作。 根据复杂度的加法原则,O(n)+O(1)=O(n)。两者的复杂度都是O(n)。
对于第二种情况,给定一个需要删除节点之后,仅需要将该节点的前节点指向该节点的下一个节点即可。

但是单向链表需要进行遍历,找到该节点的前节点。需要O(n)。所以单链表需要O(n)。但是由于双向链表每个节点有指向前节点的指针。故双向链表仅需O(1)。
其实第二种情况,是我们经常会遇到的,比如LinkedHashMap容器,就是使用了双向链表。
不仅仅时删除和插入操作,双向链表比单向链表高效。其实查询也会比单向链表高效。比如在一个有序的链表中,我们可以保存上一次查询节点,判断查询值的大小,采取向上还是从下查询的方式。
其实这就是一个空间换时间的例子,双向链表虽然比单向链表要高效,但是它比单向链表多一个指针变量。因此消耗的内存资源也会比较多。
数组和链表的选择
我觉得数组和链表的选择应该要结合以下几点因素考虑:
1. 时间复杂度
数组的随机访问时间复杂度是O(1),删除操作的复杂度O(n);链表的随机访问的复杂度是O(n),删除操作的复杂度O(1);结合你的业务逻辑,评价主要是查询操作居多还是删除操作居多。
2. 内存要求
因为链表中每个节点需要一个指向下一个节点的指针变量,所以链表比数组消耗更多的内存资源。当你的内存资源比较有限的情况下,我还是建议你使用数组。
3. 性能提高--CPU缓存机制
我们知道数组的访问比链表要高效。但是这只是从时间复杂度来分析的。但是不仅仅如此,数组在访问操作时,因为cache机制的存在,效率会更加高。因此,如果你想更进一步提高访问的效率,我建议你也选择数组。
总结:综上所述,数组有这么多的优势,我们是不是基本都选择数组呢?我们工作中基本还是选择链表较多。因为我们基本不会存在性能和内存资源上的担忧,并且链表使用起来还是比较方便的。
什么是CPU缓存机制?
上面我们谈到了CPU缓存机制,数组对CPU缓存机制比较友好能够加快访问效率,但是链表对其不友好,不能提高效率。但是何为CPU缓存机制呢?其实它是依据操作系统中局部性原理来实现的。
所谓的局部性原理,包括两个部分:时间局部性和空间局部性。时间局部性指的是最近访问的存储位置,很可能在不久的将来还要访问;空间局部性指存储访问有聚集的倾向,当访问了某一个位置之后,很有可能也要访问附近的位置。
我们知道Cache是高速访问的缓存,比内存的访问还要快。CPU从内存中访问数据并不会仅仅只获取该地址的内容。而是会将该内容在内的物理块一起放到Cache缓存中。下次再读取内容时,首先再Cahce中找,找不到再从内存中重复上面的操作。
因为数组的存储空间是连续的,所以能够很好的适应CPU缓存机制。但是链表是非连续存储的,所以对CPU缓存机制不友好。
总结
了解了数组和链表之间的区别,以及如何选择数组和链表数据结构。数组对CPU缓存机制更加友好。
相关文章:
【追求卓越02】数据结构--链表
引导 今天我们进入链表的学习,我相信大家对链表都很熟悉。链表和数组一样,作为最基础的数据结构。在我们的工作中常常会使用到。但是我们真的了解到数组和链表的区别吗?什么时候使用数组,什么时候使用链表,能够正确的选…...
qt按照不同编码格式读取文字(UTF-16LE,UTF-8,UTF-8BOM,UTF-16BE)
enum class EncodingFormat : int {ANSI 0,//GBKUTF16LE,UTF16BE,UTF8,UTF8BOM, }; EncodingFormat VideoPlayer::FileCharacterEncoding(const QString &fileName) {//假定默认编码utf8EncodingFormat code EncodingFormat::UTF8;QFile file(fileName);if (file.open(QI…...
R语言和RStudio的下载安装(非常简便舒适)
目录 R语言和RStudio的关系R语言和Tableau下载R语言进入官网选择清华镜像源Download R for Windows选择base版本开始下载进行安装配置环境变量检查是否安装成功 下载RStudio进入官网点击下载进行安装检查是否安装成功打开选择R语言环境成功打开显示四个工作区 R语言和RStudio的…...
SQL注入漏洞发现和利用,以及SQL注入的防护
一、背景 SQL注入漏洞是一种常见的软件安全问题,它发生在应用程序的数据库层中。其核心原理是将用户输入的数据当做代码来执行,违反了“数据与代码分离”的原则。具体来说,攻击者通过构造恶意的SQL查询语句,使得应用程序在执行SQ…...
Jmeter 分布式压测
为什么要分布式 jmeter是100%纯java开发的程序,虚拟用户是以线程实现的,在大量并发情况下,很容易出现CPU、内存消耗过大的问题,甚至会出现java内存溢出。一般一台电脑设置500-600线程数即可,如果超过1000线程…...
Docker 安装 Apache
目录 拉取官方 Apache 镜像 查看本地镜像 列出正在运行的容器 运行 Apache 容器 创建一个 HTML 文件:index.html 访问 Apache 拉取官方 Apache 镜像 查找 Docker Hub 上的 httpd 镜像。 可以通过 Tags 查看其他版本的 httpd,默认是最新版本 httpd…...
python变量、常量、数据类型
一、变量 变量是存储在内存中的值,这就意味着在创建变量时会在内存中开辟一个空间。 基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。 因此,变量可以指定不同的数据类型,这些变量可以…...
注册中心CAP架构剖析
Nacos 支持 AP 或 CP AP Nacos 通过临时节点实现 AP 架构,将服务列表放在内存中; CP Nacos 通过持久化节点实现 CP 架构,将服务列表放在文件中,并同步到内存,通过 Raft 协议算法实现; 通过配置 epheme…...
SVN创建分支
一 从本地创建方式可指定版本号进行分支创建。 1、在本地目录右击 -----> 点击branch/tag(分支/标签) From: 源,可指定具体的版本号, To path: 可通过"..."选择分支路径 最后点击确定,交由服务器执行创建。 二 通过SVN客…...
Vue 设置v-html中元素样式
使用方式: <<< img { max-width: 100% } 如:要将v-html中的图片元素(img)的最大宽度设置为100%. <template><div ><div class"rtfDiv book" v-html"content"></div></div> </template&…...
连接服务器的脚本
对于记不住的服务器密码且不愿用三方工具俺简单写了个脚本(检测下最近shell脚本的学习效果咋样) expect 是处理交互的一种脚本语言,spawn启动指定进程 -> expect获取指定关键字 -> send想指定进程发送指定指令 -> 执行完成后退出 sp…...
ChatGPT/GPT4丨编程助手;AI画图;数据分析;科研/项目实现;提示词工程技巧;论文写作等
ChatGPT 在论文写作与编程方面也具备强大的能力。无论是进行代码生成、错误调试还是解决编程难题,ChatGPT都能为您提供实用且高质量的建议和指导,提高编程效率和准确性。此外,ChatGPT是一位出色的合作伙伴,可以为您提供论文写作的…...
35的程序员被辞了可以自己接外包啊?为什么都那么悲观呢?
35的年纪,上有老下有小,即将步入中年危机,在这个节骨眼上被辞,能不悲观吗? 在这个年纪人们往往追求的是稳定的工作和生活,而进入一个自己不熟悉的行业并不是一个好的选择。 况且,你认为的外包…...
2020年09月 Scratch(三级)真题解析#中国电子学会#全国青少年软件编程等级考试
Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 执行下面程序,屏幕上最多会看到多少个苹果? A:10个 B:11个 C:1个 D:无法确定 答案:B 第2题 关于下面程序,说法正确的是 ? A:执行 后,马上执行...
SpringBoot面试之SpringBoot自动装配原理
SpringBoot自动装配原理 背景 最近因为各种原因,我又重新加入到了找工作的大军当中。昨天在面试的时候与面试官聊到我们项目都是基于SpringBoot开发的,然后面试官就顺口问了句:”SpringBoot项目会引入许多的starter,比如&#x…...
JavaScript:监听事件
该方法用于向浏览器窗口注册事件监听器,当指定的事件(如单击、按键按下)被触发时,浏览器会自动调用指定的函数(回调函数)。 window.addEventListener(event, function, useCapture); 参数说明:…...
编写SQL语句,场景:从一张表中查询某字段是逗号分隔的集合值,需要遍历集合内每个值,将其作为条件去查询另一张表,最终返回列表
目录 场景编写SQL分页获取该开票单号下的所有订单列表使用子查询和 in 字句使用 find_in_set 场景 从一张表中查询某字段是逗号分隔的集合值,需要遍历集合内每个值,将其作为条件去查询另一张表,最终返回列表 编写SQL 分页获取该开票单号下…...
单链表相关面试题--7.链表的回文结构
7.链表的回文结构 链表的回文结构_牛客题霸_牛客网 (nowcoder.com) /* 解题思路: 此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。 */ class PalindromeList…...
JUC(Java Util Concurrent)多线程并发库
JUC(Java Util Concurrent)是Java中用于编写多线程并发程序的库。开发过程中使用JUC主要有以下几个好处: 1. 提高程序性能:使用JUC可以实现多线程并发执行,充分利用多核CPU,提高程序的性能。 2. 简化代码…...
如何在Linux系统上检测GPU显存和使用情况?
如何在Linux系统上检测GPU显存和使用情况? 在Linux系统上,你可以使用一些命令行工具来检测GPU显存和使用情况。以下是一些常用的方法: 1. 使用nvidia-smi(仅适用于NVIDIA GPU) 如果你使用的是NVIDIA的显卡࿰…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
