数据结构与算法——数组篇

一、🍑 数组的定义

数组(Array)是一种线性表数据结构。它用一组连续的内存空间 ,来存储一组具有相同类型的数据。

1. 线性表数据结构

就是把数据排成像一条线一样的结构,每个线性表上的数据只有前和后两个方向,除了数组,像链表、队列、栈都是线性表结构,而二叉树、堆、图叫做非线性表,在非线性表中,数据不是简单的前后关系。

2. 连续的内存空间和相同类型的数据

正是因为有了这两个特性,所以数组能够'随机访问(随机选择下标,进行数据访问)',但是这两个限制也让数组的很多操作变得低效,比如在数组中删除或者插入输入,为了保证连续性,就需要做大量的数据搬移工作。

二、🍑 数组是如何实现根据下标随机访问数组元素

计算机给每个内存单元分配一个地址,计算机通过地址来访问内存中的数据,当计算机要随机访问数组中的元素时,它会首先通过寻址公式,计算出该元素的内存地址,举一个字符数组的例子,如图所示:

注意:

  • 数组下标都是从0开始的。
  • 数组内存空间的地址是连续的

三、🍑 数组和链表的区别

  1. 链表适合插入、删除,时间复杂度 O(1);
  2. 数组适合查找,因为数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

四、🍑 数组低效的"插入"和"删除"

为了保证数据在内存存储时的连续性,在插入和删除时,需要对目标位置后面的数据进行整体后移和前移,所以插入和删除这两个操作比较低效。

1. 插入

假设数组的长度为 n,现在,如果我们需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来,给新来的数据,我们需要将第 k~n 这部分的元素都顺序地往后挪一位。可以看出来插入的位置不同,会导致针对同一段代码,时间复杂度有量级的差距。

  • 最好时间复杂度:我们插入的位置位于数组的最后一位,不需要移动任何元素,最好时间复杂度为O(1)。
  • 最坏时间复杂度:我们插入的位置位于数组的第一位,需要移动n个元素,最坏时间复杂度为O(n)。
  • 平均时间复杂度:先把概率算清, 插入到任一位置的可能性都是一样的,n个位置,所以插入到每一个位置的概率都是1/n. 插入到数组的第一个位置需要移动 n个元素. 插入到数组的第二个位置需要移动 n-1 个元素,...,插入到数组中的最后一个位置,需要移动1个元素,(n+n-1+n-2+...+1)/n=(n+1)/2=O(n) 所以: 数组插入操作的平均时间复杂度为O(n)

注意:如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。在这种情况下,如果要将某个数据插入到第 k 个位置,为了避免大规模的数据搬移,我们还有一个简单的办法就是,直接将第 k 位的数据搬移到数组元素的最后,把新的元素赋值给第 k 个位置。所以特定场景下,直接调换指定的位置数据就可以了,不需要把后面的数据整体移动,这样在第 k 个位置插入一个元素的时间复杂度就会降为 O(1)。

2. 删除

跟插入数据类似,如果我们要删除第 3个位置的数据,为了内存的连续性,需要对下标为3的元素后面的所有元素都要做移动操作,否则内存就不连续了。如下图

  • 最好时间复杂度:删除最后一位,长度-1,复杂度O(1)。
  • 最坏时间复杂度:删除第一位,剩下每个数据都向左移动1个位置一共n-1个元素共执行了n-1次操作,复杂度O(n)。
  • 平均时间复杂度:概率是每个元素被删除的概率是1/n,平均操作次数:(n-1 + n-2 + n-3 + ... + 1 + 0)* 1/n = (n-1)/2。 去掉系数->复杂度O(n)。

注意:在某些特殊场景下,我们并不一定非得追求数组中数据的连续性. 将多次删除操作集中在一起执行,数组的删除效率也会提高很多。

五、🍑 为什么很多编程语言中,数组的下标都从0开始

数组"下标"最确切的定义应该是"偏移(offset)"。前面也讲到,如果用 a 来表示数组的首地址,a0就是偏移为 0 的位置,也就是首地址,ak就表示偏移 k 个 type_size 的位置,所以计算 ak的内存地址只需要用这个公式:

js 复制代码
a[k]_address = base_address + k * type_size

但是,如果数组从 1 开始计数,那我们计算数组元素 ak的内存地址就会变为:

js 复制代码
a[k]_address = base_address + (k-1) * type_size

对比一下,从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。(人像机器妥协),数组作为基础的数据结构,通过下标访问数组元素也是基础的编程操作,所以为了减少一次减法操作,提升效率优化,数组选择了从 0 开始编号,而不是从 1 开始。

总结

数组作为最基础、最简单的数据结构了。数组用一块连续的内存空间,来存储相同类型的一组数据,最大的特点就是支持随机访问,但插入、删除操作也因此变得比较低效,平均情况时间复杂度为 O(n)。

相关推荐
猫猫不是喵喵.36 分钟前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~2 小时前
CSS元素显示模式(display)
前端·css
luj_17682 小时前
星火科技助力边远地区防病攻坚
c语言·开发语言·c++·经验分享·算法
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
geats人山人海4 小时前
数据结构第六章c语言 树的存储下二叉树的概念和存储
c语言·数据结构·算法
做前端的娜娜子5 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅5 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
漂流瓶jz5 小时前
UVA-12627 奇怪的气球膨胀 题解答案代码 算法竞赛入门经典第二版
算法·图论·递归·aoapc·算法竞赛入门经典·uva·12627
jarvisuni5 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法