1. 数组:为什么很多编程语言中数组都是从0开始?

在大部分编程语言中,数组都是从0开始编号的,但你是否下意识想过,为什么数组要从0开始编号,而不是1开始呢? 从1开始不是更符合人类的思维习惯吗?下面以这个问题来学习数组。

数组的基本概念与特性

什么是数组?

什么是数组?估计你心中已经有了答案。不过,这里还是总结一下。数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据
这里定义里有几个关键词,理解了这几个关键词,就能彻底掌握数组的概念了。

线性表(Linear List)。顾名思义,线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。除了数组,链表、队列、栈等也是线性表结构。

而与它相对立的概念是非线性表,比如二叉树、堆、图等。之所以叫非线性,是因为在非线性表中,数据之间并不是简单的前后关系。

总结下数组的特性

  • 第一是线性表(Linear List)。
  • 第二是连续的内存空间和相同类型的数据。

正是因为这两个限制,它才有一个堪称“杀手锏”的特性 “随机访问” 。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一条数据,为来保证连续性,就需要左大量的数据搬移工作。

如何实现随机访问?

数组到底是如何实现根据下标随机访问数组元素的?
例如:长度为 10 的 int 类型的数组 int[] a = new int[10]

  1. 计算机给数组 a[10] ,分配了一块连续内存空间 1000 ~ 1039 ;
  2. 内存块的首地址为 base_address = 1000 ;
  3. 计算机会给每个内存单元分配一个地址,计算机通过地址来访问内存中的数据。

当计算机需要随机访问数组中的某个元素时,它会通过下面的寻址公式,计算出该元素存储的内存地址:
a[i]_address = base_address + i * data_type_size

arr[i] 首地址 = 数组内存块首地址 + 数据类型大小 * i,其中 i 为偏移量,其中 data_type_size 表示数组中每个元素的大小。

上面这个例子里面:
base_address :内存块的首地址。
data_type_size : 表示数组中每个元素的大小,比如目前数组中存储的是 int 类型数据,所以 data_type_size 就为 4 个字节。

数组时间复杂度

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

  1. 数组是一种线性表;
  2. 连续的内存空间和相同类型的数据。
    由于第二个性质,数组支持 “随机访问”,根据下标随机访问时间复杂度为 O(1),但是在数组中删除、插入数据时需要做数据搬移工作。

低效的“插入”和“删除”操作

1. 插入操作

假如数组的长度为 n,我们需要将一个数据插入到数据的第 k 个位置,则需要将 [k , n] 位元素都顺序地往后挪动一位。

  • 最好的情况:时间复杂度为O(1),此时在数组末尾插入元素。
  • 最坏的情况:时间复杂度为O(n),此时在数组开头插入元素。
  • 平均的情况:时间复杂度为O(n),因为在每个位置插入元素的概率相同,故(1+2+3+......+n)/ n = O(n)

2. 删除操作

和插入操作一样,为了保证内存的连续性,删除操作也需要搬移数据。

  • 最好的情况:时间复杂度为O(1),此时删除数组末尾的元素。
  • 最坏的情况:时间复杂度为O(n),此时删除数组开头的元素。
  • 平均的情况:时间复杂度为O(n),因为删除每个位置的元素的概率相同,故(1+2+3+…+n)/ n = O(n)。

更高级用法

在某些特殊场景下,在不追求数组中数组的连续时,我们将多次删除操作集中在一起执行,会提高删除的效率?
例如:假设有一个数组 a , 长度为 10,存储了 8 个元素,分别为 a,b,c,d,e,f,g,h 。现在我们依次删除 a,b,c 三个元素:

a = [a,b,c,d,e,f,g,h];

为了避免 d,e,f,g,h 这个几个数据会被搬移 3 次,我们先记录已经删除的数据(每次删除操作并不是真正的搬移数据,只是记录数据已经被删除)。当 a 数组没有空间存储数据时,这才触发依次真正的删除操作,这样就减少了删除操作导致的数据搬移。

上述这个操作其实就是 JVM标记清除垃圾回收算法 的核心思想。

3. 警惕数组访问越界

在 C 语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的。如果疏忽会造成严重的后果。当然,Java语言会自动检测。

4. 总结
**
数组是最基础、最简单的数据结构。数组用一块连续的内存空间,来存储相同类型的一组数据,最大特点就是随机访问元素,并且时间复杂度为 O(1)。但是插入、删除操作也因此比较低效,时间复杂度为O(n)。

数组和链表的区别

  • 数组支持随机访问,根据下标随机访问的时间复杂度为O(1),注意数组查找,即便是排好序的数组,使用二分查找时间复杂度为O(logn)。
  • 链表适合插入、删除、时间复杂度为O(1)

最后总结一下:为什么大多数编程语言中,数组要从 0 开始编号,而不是从 1 开始呢?

  • 第一:历史原因, c 语言设计者用 0 开始计数数组下标,之后 Java、JavaScript等高级语言都效仿 C 语言,因此继续沿用从 0 开始计数的习惯。部分语言数组不是从 0 开始计数的,比如 Matlab,还有部分语言支持负数下标,如 Python。

  • 第二:从数组存储的内存模型上来看,“下标”最确切的定义应该是 “偏移(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

对比两个公式,不难发现,从 1 开始编号,每次随机访问数组元素都多来一次减法运算,对于 CPU 来说,就多来一次减法指令。

数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致,所以为来减少一次减法操作,数组选择来从 0 开始编号,而不是 1 开始。

以上谢谢大家,求赞求赞求赞!

1. 数组:为什么数组要从0开始编号,而不是1开始呢?相关推荐

  1. 数组的下标为什么从0开始

    部分来源于其他博客 为什么数组的下标是从0开始,而不是从1开始呢?从1开始不是更符合人们的习惯么. 数组(Array)一种线性表数据结构,用一组连续的内存空间,存储一组相同类型的数据 线性表(Line ...

  2. linux数组shell数组添加内容,shell数组的定义与应用

    bash支持一维数组(不支持多维数组),并且没有限定数组的大小.类似与C语言,数组元素的下标由0开始编号.获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0.shell 下的多 ...

  3. linux awk 数组,shell数组和awk数组

    awk终于能入门了,所以整理了该文章,内容大多来自网上. 一.bash支持一维数组(不支持多维数组),没有限定数组的大小.在shell中,用括号来表示数组,数组元素用空格符号分割开.类似于C语言,数组 ...

  4. 扩容是元素还是数组_数组是如何随机访问元素?数组下标为什么从0开始,而不是1?...

    作者:鹏磊 来源:搜云库技术团队 数组如何实现随机访问元素 什么是数组? 数组(Array)是一种线性表数据结构,它用一组连续的内存空间,来存储相同类型的数据. 什么是线性表(Linear List) ...

  5. c语言定义数组变量初始化为0,c语言数组初始化——int a[N] = {0};

    初学数组,以华科的<c 语言与程序设计>为教材,参考了<一站式学习c编程>,后者数组一章有串代码如下 int main(void) { int i, histogram[10] ...

  6. char a[]和char *a的比较,数组名,数组首地址,a,a,a[0]

    char a[]和char *a的比较 指针和数组存在着一些本质的区别.当然,在某种情况下,比如数组作为函数的参数进行传递时,由于该数组自动退化为同类型的指针,所以在函数内部,作为函数参数传递进来的指 ...

  7. 堆初始化-二叉堆一般用数组来表示。例如,根节点在数组中的位置是0,第n个位置的子节点分别在2n+1和 2n+2-icoding-void init_min_heap(PMinHeap pq, int

    堆初始化 二叉堆一般用数组来表示.例如,根节点在数组中的位置是0,第n个位置的子节点分别在2n+1和 2n+2.  因此,第0个位置的子节点在1和2,1的子节点在3和4.以此类推.这种存储方式便于寻找 ...

  8. 05 | 数组:为什么很多编程语言中数组都从0开始编号?

    什么是数组? 数组(Array)是一种线性表数据结构.它用一组连续的内存空间,来存储一组具有相同类型的数据. 线性表存储结构 连续内存空间 存储相同类型数据 优点:连续内存+相同类型数据=数组可以实现 ...

  9. 计算payload长度c语言,C语言0长度数组(可变数组/柔性数组)详解

    1 零长度数组概念 众所周知, GNU/GCC 在标准的 C/C++ 基础上做了有实用性的扩展, 零长度数组(Arrays of Length Zero) 就是其中一个知名的扩展. 多数情况下, 其应 ...

  10. c++ 不能分配给为0的数组_【嵌入式C】你有想过quot;数组下标quot;为何从0开始吗?...

    1.聊一聊 相信大家都有看过电影,今天所分享的是其经典背景音乐,或许音乐响起你又会想起那条单纯.善良的秋田犬! 今天跟大家聊聊一个有意思的话题,C中的数组下标为啥是从0开始?或者说为什么现在大部分的编 ...

最新文章

  1. 业界 | 德勤预测:机器学习走向移动端成大势所趋,或将再掀行业新浪潮
  2. 【知识发现】python开源哈夫曼编码库huffman
  3. python mp3操作
  4. 用于计算机安全防护的有,《计算机安全防护》PPT课件.ppt
  5. C#计算两个时间的差
  6. POJ - 2676 Sudoku(dfs)
  7. Java:switch语句例子
  8. sqlserver日期函数 dateadd,datediff ,datepart ,datename,convert
  9. Android.mk、Makefile、Cmake打印log
  10. 视频编解码(九):FFMPEG操作总结一
  11. avast! Virus Cleaner 1.0.210
  12. Selenium之浏览器驱动下载和配置使用
  13. unity怎么制作云飘动_现实的动态云系统特效脚本Unity3D素材资源
  14. Mstar 6A628 ubuntu 14.04 server Android 开发环境搭建
  15. Java内存模型(JMM)学习总结
  16. 分布式:分布式系统设计实践。
  17. java: 未报告的异常错误java.lang.IllegalAccessException; 必须对其进行捕获或声明以便抛出
  18. 防沉迷与身份证系统挂钩 网游要实名认证
  19. c++ mfc实现中英文菜单的动态切换
  20. 网易云音乐React Native体系建设与发展

热门文章

  1. 深入浅出理解数据库s锁和x锁
  2. Java招聘网站源码+页面
  3. java输出执行开始时间,结束时间和运行时间
  4. wfp网络过滤框架总结(一)
  5. HTML5+CSS3制作透视正方体
  6. iscc 2021wp
  7. Parallels Desktop 16 网络初始化失败
  8. 记一个eclipse快捷键Alt+Shift+L
  9. 花旗报告揭秘2016全球FinTech变革全景、“AI如何落地”公开课分享 | AI金融评论周刊...
  10. quartz动态任务调度实现