数组的那些事儿

数组在任何一种编程语言中,都是一种基础的数据结构。大家提到数组,都不会感到陌生,甚至会拍拍胸脯自信地说:这很简单啊。

大家有没有想过:数组为什么从 0 开始编号呢,为什么不从 1 开始呢?希望你带着这个疑问去看接下去的文章。

文中会涉及到时间复杂度的分析,不懂的朋友可以先看看这篇。
复杂度分析

什么是数组?

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

这句话里有几个关键词,下面我就来给你解读一下。

1. 线性表(Linear List)。顾名思义,线性表就是数据排成一条线一样的结构。链表、队列、栈等数据结构也是线性表结构,我会在后文中一一讲解,敬请关注哦。而堆、树、图等则是非线性的,即数据间不是简单的前后关系。

2. 连续的内存空间,相同的数据类型。正是由于数组的这个杀手锏特性,数组可以实现随机访问,但是任何一种数据结构都是有其适用场合的,有利有弊的。数据的插入和删除操作,为了保证连续性,需要做大量的工作。

而数组的随机访问由寻址方式实现:

a[i]_address = base_address + i * data_type_size

data_type_size 为数组中每个元素的大小,如果数组中存储 int 类型数据, data_type_size 为 4 个字节。

举个例子:现在我们创建一个长度为 5 的 int 类型的数组,int[] a = new int[5]; 计算机会在内存中给数组 a[5] 分配一块连续的内存,我们设内存块的首地址为 base_address 为 1000,即

int a[5] 内存地址
a[0] 1000~1003
a[1] 1004~1007
a[2] 1008~1011
a[3] 1012~1015
a[4] 1016~1019

当计算机需要访问元素 a[1] 时,它会根据上面的寻址公式,即:

a[i]_address = base_address + i * data_type_size

来计算出 a[1] 的内存地址,从而实现随机访问,根据下标访问时间复杂度为 O(1)。

另外在面试中面试官经常会问数组和链表的区别,我们可以这样回答:
链表适合插入、删除,时间复杂度为 O(1);数组适合查找,但是查找的时间复杂度并不为 O(1),即使是排好序的数组,你用二分查找,时间复杂度也是 O(logn)。

数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

数组的插入和删除操作

我们先来看插入操作。

如果我们在数组的末尾插入数据,我们不需要移动数据,时间复杂度为 O(1)。我们在数组的开头插入数据,需要移动 n 个数据,时间复杂度为 O(n)。假设我们在数组的任何位置插入数据的概率是相同的,则平均时间复杂度为 (1+2+3+···+n) / n = O(n)。

我们再来看删除操作,和插入类似,删除数组最后一个数据,时间复杂度为 O(1)。删除开头第一个数据,需要移动 n 个数据,时间复杂度为 O(n)。平均时间复杂度为 (1+2+3+···+n) / n = O(n)。

我们看到数组的插入和删除操作都是比较低效的,而这都是数组的连续性带来的。实际上,在某些特殊场景里,我们不一定会去追求数组中数据的连续性,如果我们可以将多次的删除集中在一起执行呢?如果这样做的话,执行效率是不是高了很多呢?

我们继续延用上面的例子给你解释:
假设数组 a[5] 存储了 5 个元素:1, 2, 3, 4, 5。现在,我们要依次删除 1, 2, 3 三个元素。我们可以先记录下已经删除的数据,每次的删除操作并不是真正地搬移数据,只是删除记录数据。当数组没有更多空间存储数据时,我们触发一次真正的删除操作,这样执行效率大大增加。

假如你了解 JVM 的垃圾回收算法,你会发现这就是它的核心思想。
数据结构与算法的最大魅力莫过于此,我们可以在很多地方发现他的影子,是程序的灵魂,也会有很多我们意想不到的「骚操作」。

容器能否代替数组

我们知道很多语言都提供了容器类,由于笔者比较熟悉 Java,这里我拿 Java 举例,java 中的 ArrayList 作为 Java 工程师几乎天天都在使用。我们知道 ArrayList 封装了很多数组操作的细节,改用方法代替,还有就是支持动态扩容,我们完全不用担心底层逻辑,当我们的空间不够用时,ArrayList 会自动帮我们将空间扩容成 1.5 倍。

但是 ArrayList 是无法存储基本类型的,如 int、long,需要封装成 Integer、Long 类,另外对于数据大小我们事先已知,数据操作较为简单,我们可以优先考虑数组,毕竟我们不需要用到 ArrayList 提供的大多数方法。

最后,我来回答大家这个问题:数组为什么从 0 开始编号呢,为什么不从 1 开始呢?

答曰:历史原因,因为 C 语言的设计者设计数组时是从 0 开始的,后来 Java 等高级语言都效仿 C 语言,再说,也让 C 语言学习者转入其他高级语言的学习减少了成本。怪不得,学校老师总是要求大家好好学 C 语言呢?

今天数组的学习就到这里了,我留个小问题,请你思考一下,二维数组的寻址是怎样的呢?

欢迎留言与我分享!

System.out.println("点个喜欢!欢迎关注我!");

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容