【数据结构与算法】线性表--数组
文章目录
- 一、前言
- 二、数组的概念
- 三、数组的操作
- 数组的插入
- 数组的删除
- 四、容器与数组
- 五、问题:为何数组要从0开始编号,而不是1开始呢?
- 六、总结
一、前言
常见的数据结构如下图,本文主要讲解数据结构线性表--数组。

二、数组的概念
定义:
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
从概念中可以知道一下几点:
- 数组是线性表
所谓的线性表就是数据排成一排,想一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。当然除了数组,链表、队列、栈等也是线性表结构

- 连续的内存空间和形同类型的数据。
正因为有了上述两个特点,数组才能够有一个堪称“杀手锏”的特性:随机访问
数组实现下标随机访问
下面通过一个实际的例子来说明:
例如有一个长度为10的int数组,int[] a = new int[10].

计算机给数组a[10]分配了一块连续的内存空间1000~1039,其中,内存块的首地址为base_address = 1000.
计算机会为每个内存单元分配一个地址,计算机通过地址来访问内存中的数据。当计算即需要随机访问数组中的某个元素的时候,它会首先通过下面的寻址公式,计算该元素存存储的内存地址:
a[i]_address = base_address + i * data_type_size
其中data_type_size表示数组中每个元素的大小。
例如,数组中存储的int类型的数据,所以,data_type_size就是4字节。
三、数组的操作
通常我们都会说,“链表适合插入、删除。时间复杂度为O(1);数组适合查找,查找时间复杂度为O(1)”,其实该种说法是不对的,正确的应该是:数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。
因为,数组是适合查找操作,但是查找的时间复杂度并不是O(1),即便是排好序的数组,使用二分查找,时间复杂度也是O(logn)
数组为了保持内存的数据的连续性,会导致插入、删除这两个操作比较低效。
数组的插入
分为三种情况:
-
数组头插入
当我们在数组的头部插入一个元素的时候,那么所有的元素都需要向后挪一位

该种情况下的最坏时间复杂度为O(n)。 -
数组中间插入
在该种情况下,如果要将某个元素插入到数组中的第k个位置,就必须按照上一种方式搬移k之后的数据。

该种情况下数据插入的最坏时间复杂度为O(n)。
但是如果说,数组只是被用来当做一个存储集合,而不考虑数组中数据的顺序的话,为了避免大规模的数据搬移操作,有一个简单的办法,就是直接将第k个位置的数据搬移到数组的最后,然后将新元素直接放到第k个位置。例如:

该种处理技巧,在特定场景下,在第k个位置插入一个元素的时间复杂度会降为O(1)。
- 数组尾部插入
如果是在数组尾部插入元素的话,此时,就不需要移动数据

该种情况下,元素插入的时间复杂度为O(1)
数组的删除
其实数组的删除操作,跟插入操作类似,同样分为三种:
- 删除头部元素
该种情况下,因为也数组也需要保持内存空间的连续性,所以也需要搬移数据,最坏时间复杂度为O(n) - 删除中间位置元素
这种情况下,其实跟删除头部元素类似,数组为了保持内存的连续性,也需要搬移数据,不然中间会出现空洞,内存就不连续了,最坏时间复杂度也为O(n) - 删除尾部元素
如果说是删除末尾元素的话,此时数组其他的数据不需要进行移动,时间复杂度为O(1)
但是在某些特殊场景下,我们并不一定需要非得追求数组中数据的连续性。如果我们将多次删除操作集中在一起执行,删除的效率是不是会提高很多呢,比如说:
现在有个数组a[10]中存储了8个元素:a,b,c,d,e,f,g,h,现在要依次删除a,b,c三个元素

为了避免数据d,e,f,g,h这几个数据搬移三次,我们可以先记录下已经删除的数据。每次的删除操作并不是真正的搬移数据,只是记录数据已经被删除。当数组中没有更多的空间的时候,我们再触发一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。
这种形式貌似跟HBase的删除操作有点类似,在每次执行delete的时候,并不是真正的从region当中执行删除操作,而是先给要删除的记录打个标记,最后在合适的时间统一进行移除底层的hfile中的数据文件
同样,如果对jvm了解的话,会发现,这种情况也同JVM的标记清除垃圾回收算法的核心思想相类似。
四、容器与数组
大多数语言中提供了容器类,比如java中提供了ArrayList,但是具体与数组相比较,优势在哪里呢?
- ArrayList
ArrayList最大的优势就是可以将很多数组操作的细节封装起来.
ArrayList支持动态扩容,每次存储空间不够的时候,会自动将空间扩容为原来的1.5倍大小。但是同样,动态扩容涉及到内存的申请和数据的搬移,也是比较耗时的。所以,如果实现能确定好需要存储的数据大小,最好在创建Arraylist的时候事先指定数据大小。 - 数组
数组本身在定义的时候需要预先指定大小,因为需要分配连续的内存空间。如果我们申请了大小为10的数组,当第11个数据需要存储到数组中时,我们就需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入。
五、问题:为何数组要从0开始编号,而不是1开始呢?
从数组的模型上来看的话,“下标"嘴确切的定义应该是"偏移(offset)”.也就是说,如果用a来表示数组的话,a[0]就表示偏移为0的位置,也就是首地址,a[k]就表示偏移为k个type_size的位置,所以计算a[k]的内存地址只需要使用下面的这个公式即可:
a[k]_address = base_address + k * type_size
如果是从1开始编号的话,那么计算数组元素a[k]的内存地址就成了:
a[k]_address = base_address + (k-1) * type_size
对于当中的参数的含义,base_address:数组的首地址,正如文章开头画的图表示的事1000,k表示的则是数组的下标,type_size则表示数组存储的数据的大小,如果存储的是一个int型的数据,那么就是4字节。
对比上面两个公式,并结合文章开头说话的图验证一下,可以发现,从1开始编号,每次随机访问数组元素都多了一次减法运算,对于CPU来说的话,那就是多了一次减法指令。
数组作为非常基础的数据结构,通过下标随机访问数组元素有事非常基础的编程操作,效率的优化就要尽可能的做大极致。所以为了减少一次减法操作,数组选择了从0开始编号,而不是从1开始编号。
六、总结
-
Java ArrayList无法存储基本数据类型,比如
int,Long,需要封装为Integer,Long类,而Autoboxing,Unboxing择优一定的性能消耗,所以如果特别关注性能,或者希望使用基本类型,既可以选用数组。 -
如果数据大小事先已知,并且对数据的操作非常简单,用不用到ArrayList提供的大部分方法,也可使用 数组。
-
当要表示多维数组时,用数组往往会更加直观。比如Object[][] array;而用容器的话则需要这样定义:
ArrayList<ArrayList> array.
相关文章:
【数据结构与算法】线性表--数组
文章目录一、前言二、数组的概念三、数组的操作数组的插入数组的删除四、容器与数组五、问题:为何数组要从0开始编号,而不是1开始呢?六、总结一、前言 常见的数据结构如下图,本文主要讲解数据结构线性表--数组。 二、数组的概念 …...
剑指offer排序专题
剑指offer排序专题 jz3 数组中重复的数字描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[…...
已解决Cannot open D:\Soft\Python36\Scripts\pip3-script.py
已解决Cannot open D:\Soft\Python36\Scripts\pip3-script.py 文章目录报错问题报错翻译报错原因解决方法1:easy_install 来安装pip解决方法2:本地安装pip《100天精通Python》专栏推荐白嫖80g Python全栈视频报错问题 粉丝群里面的一个小伙伴遇到问题…...
3 步走,快速上手 API 接口测试
开始 API 接口测试之前,我们需要弄清接口测试的含义: 接口测试就是根据接口清单,模拟客户端向服务端发送请求数据,并获取响应数据后,查看响应数据是否符合预期的过程。 整个过程可以分为三个步骤: 第一步&…...
爬虫-day1-正则表达式作业
利用正则表达式完成下面的操作: 一、不定项选择题 能够完全匹配字符串"(010)-62661617"和字符串"01062661617"的正则表达式包括(ABD ) A. r"\(?\d{3}\)?-?\d{8}" B. r"[0-9()-]" C. r"[0-9(-)]*\d*&…...
【半监督医学图像分割 2023 CVPR】RCPS
文章目录【半监督医学图像分割 2022 CVPR】RCPS摘要1. 介绍2. 相关工作2.1 医学图像分割2.1 半监督学习2.3 对比学习3. 方法3.1 整体概述3.2 纠正伪监督3.3 双向Voxel对比学习。4. 实验【半监督医学图像分割 2022 CVPR】RCPS 论文题目:RCPS: Rectified Contrastive …...
【UVM实战练习项目】2、UVM验证环境基本框架搭建(实例一)(纯软件环境,方便日后测试使用)
本节基于DUT完成UVM验证环境的基本框架搭建,实现对UVM理论知识点进行巩固练习,具体内容包括:如何创建激励、如何建立sequencer、如何连接sequencer和driver,如何集成agent、如何构建env等。 正式开始之前让我们再来回顾下搭建验证环境的过程:首先进行数据建模sequence_ite…...
【web前端初级课程】第四章 什么是JavaScript
目录 一、JavaScript在前端的三种写法 二、常见的弹框 三、变量 四、常量 五、数据类型 六、运算符 七、循环及函数 八、相关练习 前言 JavaScript是一个面向对象的,弱数据类型的,解释型的,动态脚本语言。 面向对象更符合我们对事物…...
数字中国建设进行时:吉林大学党委常务副书记冯正玉一行调研实在智能
两会前夕,中共中央、国务院印发了《数字中国建设整体布局规划》,明确了加快数字中国建设的重点任务。《规划》强调,要加强整体谋划、统筹推进,把各项任务落到实处。在强化人才支撑的第四要点上,指出统筹布局一批数字领…...
面试官灵魂拷问[二]:SQL 语句中 where 条件后写上 1=1 是什么意思?
面试官灵魂拷问系列又来更新啦! “SQL 语句中 where 条件后写上 11 是什么意思?” 这玩意就跟很多新语言支持尾部逗号的原理一样的。 比如 Kotlin 支持数组写成 [1, 2, 3, 4, ] ,注意4后边那个逗号,为什么呢?因为当你增加一个项…...
进程与线程的关系
一、 进程 进程(Process)是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完成过程,这个过程也是进程本身从产生、发展至消亡的过程。 操作系统同时管理一个计算机系统中的多个进程,让计算机…...
自定义异常
自定义异常 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。在程序中使用自定义异常类,大体可分为以下几个步骤: 创建自定义异常类。在…...
基于springboot物资管理系统(程序+数据库)
大家好✌!我是CZ淡陌。一名专注以理论为基础实战为主的技术博主,将再这里为大家分享优质的实战项目,本人在Java毕业设计领域有多年的经验,陆续会更新更多优质的Java实战项目,希望你能有所收获,少走一些弯路…...
蓝桥杯Web组备赛笔记6
目录 一、ElementUI 1、安装 2、简单使用 3、例子 4、其他内容的学习 二、echarts 1、简介 2、考点 3、安装 4、配置项:使用echarts的三步走 5、13届蓝桥真题(3)布局切换 6、数据格式处理:14届蓝桥模拟赛 1 期&#x…...
python控制语句
🍋在本次的博客当中,我们来认识一下python语言的新的部分——python语言的控制语句。在我们的python语言当中控制语句大致分为三类:1.选择语句,2.循环语句,3.跳转语句。当我们在编写代码的时候可以根据代码的逻辑的需求…...
华为OD机试题【最小叶子节点】用 Java 解 | 含解题说明
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典本篇题目:最小叶子节点 题目 二叉树也可…...
【linux】多线程控制详述
文章目录一、进程控制1.1 POSIX线程库1.2 创建线程pthread_create1.2.1 创建一批线程1.3 终止线程pthread_exit1.4 线程等待pthread_jion1.4.1 线程的返回值(退出码)1.5 取消线程pthread_cancel1.6 C多线程1.7 分离线程pthread_detach二、线程ID值三、线…...
SpringCloud学习-实用篇01
以下内容的代码可见:SpringCloud_learn/day01 1.认识微服务 单体架构和分布式架构 体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署 优点:架构简单,部署成本低缺点:耦合度高 分布式架构&#…...
如何使用python删除一个文件?好用到上头.....
人生苦短,我用python 若想利用python删除windows里的文件, 这里需要使用os模块 那接下来就看看利用os模块是如何删除文件的吧~ 具体实现方法如下! 更多学习资料:点击此处跳转文末名片获取 os.remove(path) 删除文件 path. 如果path是一…...
java学习笔记——权限修饰符、内部类
2.1 概述 在java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限, public:公共的 protected:受保护的 default:默认的 private:私有的 2.2 不同权限的…...
临近起飞,在哪个平台更容易捡漏特价机票?2026年实测指南
“机票越临近起飞越便宜”——这个说法你一定听过。每逢假期临近,总有人在社交媒体上分享自己“起飞前两小时抢到白菜价机票”的神奇经历。但当你真的想在清明、五一出行前“赌一把”时,往往发现价格不仅没降,反而翻倍了。那么问题来了&#…...
FLUX.1-dev FP8量化模型:让AI绘画不再依赖高端显卡
FLUX.1-dev FP8量化模型:让AI绘画不再依赖高端显卡 【免费下载链接】flux1-dev 项目地址: https://ai.gitcode.com/hf_mirrors/Comfy-Org/flux1-dev 还在为显卡显存不足而无法体验最新AI绘画技术而烦恼吗?FLUX.1-dev FP8量化模型正是为你量身打造…...
终极MCP服务器指南:解锁AI智能决策的完整工具箱 [特殊字符]
终极MCP服务器指南:解锁AI智能决策的完整工具箱 🚀 【免费下载链接】servers Model Context Protocol Servers 项目地址: https://gitcode.com/GitHub_Trending/se/servers MCP服务器(Model Context Protocol Servers) 是现…...
coze-loop新手指南:无需配置,开箱即用的代码优化工具
coze-loop新手指南:无需配置,开箱即用的代码优化工具 1. 为什么你需要一个代码优化助手 想象一下这样的场景:你刚刚写完一段功能代码,运行起来没问题,但总觉得哪里不够完美。可能是执行速度不够快,或者代…...
Apollo LQR横向控制实战:从Simulink到C++的完整迁移指南(附ROS节点代码)
Apollo LQR横向控制工程化实战:从仿真验证到C落地的深度解析 在自动驾驶系统的开发流程中,控制算法的工程化落地往往是最具挑战性的环节之一。本文将聚焦LQR(线性二次调节器)算法在Apollo开源框架中的实际应用,通过完…...
手把手教你搞定CMT2300A的315MHz匹配电路:从原理图到物料清单(附实测数据)
深入解析CMT2300A在315MHz频段的射频匹配电路设计与实战优化 作为一名长期深耕射频硬件设计的工程师,我最近在工业遥控器项目中遇到了一个典型挑战:如何为CMT2300A设计稳定可靠的315MHz匹配电路。与常见的433MHz应用不同,315MHz频段在元件参数…...
图表数据提取的智能转换革命:从像素到数据点的精准跨越
图表数据提取的智能转换革命:从像素到数据点的精准跨越 【免费下载链接】WebPlotDigitizer WebPlotDigitizer: 一个基于 Web 的工具,用于从图形图像中提取数值数据,支持 XY、极地、三角图和地图。 项目地址: https://gitcode.com/gh_mirror…...
泛微E9流程表单转PDF/HTML实战:手把手教你集成档案系统(附完整代码)
泛微E9流程表单转PDF/HTML全流程开发指南:从原理到实战 在企业管理数字化转型的浪潮中,OA系统与档案系统的无缝对接已成为提升组织效能的刚需。作为国内主流的协同办公平台,泛微E9的流程表单承载着企业核心业务流程数据,如何将这些…...
LFM2.5-1.2B-Thinking部署教程:3步实现Python爬虫数据智能处理
LFM2.5-1.2B-Thinking部署教程:3步实现Python爬虫数据智能处理 1. 引言 你是不是经常遇到这样的问题:爬虫抓取了一大堆数据,但面对杂乱无章的文本内容却无从下手?手动整理不仅耗时耗力,还容易出错。现在,…...
轻量级OpenClaw监控:nanobot镜像运行状态仪表盘搭建
轻量级OpenClaw监控:nanobot镜像运行状态仪表盘搭建 1. 为什么需要监控OpenClaw运行状态 上周我在本地部署了基于nanobot镜像的OpenClaw环境,用来对接Qwen3-4B模型实现自动化办公。刚开始使用时一切顺利,直到某天早上发现OpenClaw服务已经停…...
