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
其中 data_type_size 表示数组中每个元素的大小。在这里,array中类型为 int, 所以 data_type_size为4个字节。
2.数组插入
假设数组长度为n, 插入的位置为k,则 k 的取值范围为 [0, n-1]。
最好
: k最后一个位置,不需要移动数据,复杂度为 O(1).
最坏
: k为第一个位置,需要移动n个数据,复杂度为O(n).
平均
: 插入arr每个位置的概率是一样的,则 (1+2+3+…n)/n = (n+1)/2 -> O(n)
3.数组删除
假设数组长度为n, 插入的位置为k,则 k 的取值范围为 [0, n-1]。
最好
: k最后一个位置,不需要移动数据,复杂度为 O(1).
最坏
: k为第一个位置,需要移动n个数据,复杂度为O(n).
平均
: 插入arr每个位置的概率是一样的,则 (1+2+3+…n)/n = (n+1)/2 -> O(n)
4.容器与数组比较
容器(比如ArrayList等)是否能够完全替代数组?ArrayList的优势
- 动态扩容
- 封装对数组的操作细节,操作便利
5.数组编号为啥从0开始
5.1 原因1
从数组存储的内存模型上看,“下标”最确切的定义是“偏移(offset)”。下标从0开始的寻址公式:
a[k]_address = base_address + k * type_size
但是,如果数组从1开始计数,计算数组元素a[k]的内存地址会变为:
a[k]_address = base_address + (k-1) * type_size
两者的区别是,从1开始,cpu多了一次 (k-1)的减法指令。数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致。所以为了减少一次减法操作,数组选择了从 0 开始编号,而不是从 1 开始。
5.2 原因2
历史原因,C语言从0开始。