【数据结构与算法】线性表--数组
文章目录
- 一、前言
- 二、数组的概念
- 三、数组的操作
- 数组的插入
- 数组的删除
- 四、容器与数组
- 五、问题:为何数组要从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 不同权限的…...
5月12日直播 | CANN Bench:为昇腾算子评测立起一把统一的尺子
CANN Bench:为昇腾算子评测立起一把统一的尺子 当 Coding Agent 一次写出几十个算子已成为常态,"什么算优质算子"变成了一个单一维度无法评估准确的问题:能不能过编译只是入场券,精度是否经得起验证、换个 shape 换个 d…...
避坑指南:SciencePlots安装后样式不生效?手把手教你排查Matplotlib的stylelib路径问题
科学绘图样式失效?彻底解决Matplotlib样式库路径配置难题 当你第一次尝试用SciencePlats的science样式美化科研图表时,却发现Python报出KeyError: science is not a valid style的错误提示——这种挫败感我深有体会。作为每天与数据可视化打交道的从业者…...
软件设计原则之DIP依赖倒置原则
(DIP) 依赖倒置原则 Dependency Inversion Principle核心原则抽象不应该依赖细节;细节应该依赖于抽象。场景描述在一个应用程序 Application 中需要使用到数据库,比如我们此时需要使用到 Mysql 数据库。Mysql 数据库分别具有连接,断开关闭&am…...
2026年项目管理工具测评:10款主流软件对比与企业选型建议
本文测评 ONES、Tower、Jira、Asana、monday、ClickUp、Notion、Trello、Microsoft Project、Smartsheet 十款项目管理工具,帮助选型人员从组织规模、项目复杂度、协作方式与治理需求出发,判断哪类项目管理工具更适合自身团队。一、10款项目管理工具速览…...
基于确定性脚本与LLM决策的AI多智能体自动化监控系统设计与实践
1. 项目概述:一个为AI多智能体协作而生的“自动化监工”如果你正在用OpenClaw这类框架玩多AI智能体协作,大概率会遇到一个头疼的问题:怎么知道这群“数字员工”到底在不在干活?谁在摸鱼?任务到底完成了没有?…...
从雨篷结构事故处理谈幕墙钢结构的概念设计
从雨篷结构事故处理谈幕墙钢结构的概念设计 雨篷结构设计是幕墙钢结构设计最重要内容。但由于雨篷静定结构体系的先天不足,外加设计师理论认识水平与设计经验的限制、施工时的不当行为,经常造成工程事故。这些设计缺陷和工程事故的发生,多是由于对雨篷进行概念设计时认知不…...
PDPI Spec:规格驱动开发协议,让AI编程告别“氛围编码”
1. 项目概述:从“感觉对了”到“规格对了”在软件开发的江湖里,我们可能都经历过这样的场景:产品经理丢过来一个模糊的需求,开发同学凭着一腔热血和“感觉对了”的直觉,一头扎进代码里。几周后,功能上线了&…...
如何在浏览器中重新解锁微信网页版?一款开源插件为你提供完美解决方案
如何在浏览器中重新解锁微信网页版?一款开源插件为你提供完美解决方案 【免费下载链接】wechat-need-web 让微信网页版可用 / Allow the use of WeChat via webpage access 项目地址: https://gitcode.com/gh_mirrors/we/wechat-need-web 还在为微信网页版无…...
STM32模拟I2C驱动PCF8591避坑指南:为什么你的AD/DA数据总在跳?
STM32模拟I2C驱动PCF8591避坑指南:为什么你的AD/DA数据总在跳? 调试STM32与PCF8591的模拟I2C通信时,AD/DA数据跳动是开发者最常遇到的棘手问题。本文将深入分析数据不稳定的根源,并提供一套完整的解决方案。不同于基础教程&#x…...
如何快速掌握京东自动评价工具:面向新手的完整指南
如何快速掌握京东自动评价工具:面向新手的完整指南 【免费下载链接】jd_AutoComment 自动评价,仅供交流学习之用 项目地址: https://gitcode.com/gh_mirrors/jd/jd_AutoComment 在快节奏的电商购物时代,你是否也曾为堆积如山的待评价订单而烦恼&a…...
