数据结构之数组 & 数组下标为什么从0开始,我骄傲了吗?

初学者肯定有个疑问,为什么数组起始编号使用0开始的而不是1,这主要是历史原因

首C语言设计者从数组存储的内存模型来看,“下标”更确切的定义应该是“偏移 offset”,a[0]就是偏移为0的位置,自从他们开始使用0下标后,后面的Java等语言也效仿C语言,但也有一些语言的数组不是从0开始计数的,比如MATLAB,而且Python小老弟还说:我还支持负下标呢,那我骄傲了吗?

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

解释一下定义中的名词:

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

与此相对的就是非线性表,他们的数据之间并不是简单的前后关系,比如二叉树、堆、图等。

**连续的内存空间和相同类型的数据:正因为这两个限制,数组才可以有“随机访问”**这个特性,但这两个限制也让数组的许多操作效率很低,比如数组中删除、插入一个数据,为了保证连续性,就要进行大量的数据搬移工作。

但是数组是如何根据下标实现随机访问数组元素的呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-voTj5Fq9-1616464845305)(未命名.assets/image-20210323093528863.png)]

数组是适合查找操作,但是查找的时间复杂度并不是O(1),即使是排好序的数组用二分查找,时间复杂度也是O(logn),正确的表达是:数组支持随机访问,根据下标随机访问的时间复杂度是O(1)

说完了数组的“随机访问”,再聊聊数组的插入、删除为何低效

**插入:**假设一个数组长度为n,将一个数据插入到数组的第k个位置,为了把第k个位置腾出来,要把第k~n个位置都往后挪一位。所以最好情况是在尾部插入时间复杂度是O(1),最坏是头插入时间复杂度是O(n),平均时间复杂度是O(n)。

**删除:**要删除第k个位置,为了保证内存的连续性,要把第k~n个数据向前挪一位,情况和插入一样。

提问:在Java中会有数组越界的情况,在C语言中也会报错吗?

答案是不会报错,因为在C语言中只要不是访问受限的内存,所有的内存空间都是可以自由访问的,数组越界在C语言中是一种未决行为,并没有规定数组访问越界时编译器应该如何处理。因为访问数组的本质就是访问一段连续内存,只要数组通过偏移计算得到的内存地址是可用的,就不会报错的。

那我作为一个Java工程师,几乎每天用到最多的就是ArrayList,好像也不经常用数组啊,这两个有什么区别吗?

首先针对数组类型,许多语言都提供了容器类,比如Java中的ArrayList、C++STL中的vector。

ArrayList最大的优势就是将数组的许多操作细节封装起来,而且它还支持动态扩容。

因为数组本身定义的时候需要指定大小,如果插入数据时内存空间不够,需要重新分配一块更大的空间,将原来的数据复制过去再插入新的数据。

虽然ArrayList封装了许多细节,但有时候直接使用数组会更合适:

  1. Java的ArrayList无法存储基本数据类型,像int、long都要封装成Integer、Long,使用过程中会装箱、拆箱造成一定的性能损耗,所以如果对性能要求高或者想用基本类型,就去用数组
  2. 如果数据大小已知,并且数据的操作简单,用不到ArrayList提供的方法,就可以直接使用数组
  3. 使用多维数组的时候,用int[][]会比容器定义的ArrayList<ArrayList>更加直观
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值