数据结构-数组

数组是我们开发中最常用的数据结构之一,在大部分编程语言中数组的下标都是从0开始的,为什么不是从1开始呢?

1,数组如何实现随机访问?

    首先说下数组的定义:数组是一种线性的数据结构,它用一组连续的内存来存储相同类型的数据。

    数据的定义涉及到两个关键字,线性结构和连续内存,下面分析下这两个关键字

   1.1 线性结构

      线性结构就是数据排成像一条线一样的数据结构,常见的线性数据结构有数组、链表、队 列、栈等也是线性表结构。

  1.2 连续内存

      拿一个长度为10的int类型的数组int[] a = new int[10]来举例。在我画的这个图中,计算机给数组a[10],分配了一块连续内存空间1000~1039,其中,内存块的 首地址为base_address = 1000。

计算机会给每个内存分配单元分配一个内存地址,计算机通过地址来访问内存中的数据。当计算机需要随机访问数组中的元素时,它会通过下面寻址公式,计算出存储元素的内存地址:

a[i]_address = base_address + i * data_type_size(每个元素的大小)

通过寻址公式,数组实现来随机访问元素,随机访问元素的时间复杂的是O(1)。

2,低效的删除与插入

数组为了保持内存的连续性,会导致删除与插入元素操作比较低效。我们现在就来分析一下,究竟为什么会导致低效?

我们先来看插入操作:

假设有一个长度为n的数组,我们现在需要将一个元素插入到数组中第k个位置。为了将第k个位置空出来给新插入的元素,我们需要将k~n位置的所有元素全部往后移一位,时间复杂的是O(n);

我们再来看删除操作:

跟插入操作类似,如果我们需要删除第k个元素,为了保持内存的连续性,需要将元素全部往前移动一位,时间复杂的同样也是O(n);

3,数组删除性能提升

在某些场景上,我们不一定非得追求数组的连续性。如果我们需要删除多个元素,将这几次删除操作合并在一起,删除的效率就会得到提升。

举个例子:

假设数组a[10]中存储了8个元素:a,b,c,d,e,f,g,h。现在,我们要依次删除a,b,c三个元素,为了避免d,e,f,g,h这几个数据会被搬移三次,我们可以先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数 组没有更多空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。

实际上,这就是JVM标记清除垃圾回收算法的核心思想。

4,为什么不是从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

对比两个公式,我们不难发现,从1开始编号,每次随机访问数组元素都多了一次减法运算,对于CPU来说,就是多了一次减法指令。 数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致。所以为了减少一次减法操作,数组选择了从0开始编号,而不是从1开始。

原因2:

C语言设计者用0开始计数数组下标,之后的Java、JavaScript等高级语言都效仿了C语言,或者说,为了在一定程度上减少C语言程序员学习Java的学习成本,因此 继续沿用了从0开始计数的习惯。实际上,很多语言中数组也并不是从0开始计数的,比如Matlab。甚至还有一些语言支持负数下标,比如Python。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值