活动地址:CSDN21天学习挑战赛
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
支持随机访问,根据下标随机访问的时间复杂度为 O(1)。
连续的内存空间、相同类型的数据:因此可以随机访问,但为了保证内存的连续性,需要做大量的数据搬移工作,使增删等操作变得非常低效。
线性存储,程序执行效率高。
插入元素:O(n),所插入位置后面的元素都要向后移动。
删除元素:O(n),所删除位置后面的元素都要向前移动。
访问元素:O(1),使用索引访问。
查找元素:与使用查找算法有关,如顺序查找、二分查找、插值查找等。
数组只用来存储指定类型的数据,所以存储n个数据的空间复杂度为O(n)。
数组插入数据,特定场景,特殊对待,如下:
数组需要保持有序:将插入位置k后面的数据元素依次后移,空出要插入的位置,再将新元素插入。O(n)
数组不需要保持有序:将插入位置的旧元素移到最后,再在k处插入新元素。或直接将新元素放在最后。O(1)
数组删除数据,特定场景,特殊对待,如下:
考虑内存连续性:
数组需要保持有序:删除k位置元素后,将后面的元素依次前移一个位置。O(n)
数组不需要保持有序:用最后一个元素覆盖要删除的元素。O(1)
不考虑内存连续:

(一)存有abcdef的数组,将bce标记为删除(注意:此时数组的长度仍为6),此时想在数组末尾存入g
(二)因空间不够,将没有删除的元素df依次移动到a后面a[1]、a[2]的位置,并将数组长度标记为3(注意:此时a[3]、a[4]、a[5]的内存中仍存有def,只是不再计入数组长度)
(三)将g存入a[3]的位置。
这里澄清两个概念,数组空间和数组长度:
以存储整型数据为例,

对于数组int a[],其地址为a,第一个数组元素的地址和数组的地址相等,第二个数据元素的地址为a+4(由于存储整型,所以加4),依次类推,每往后移动一个数据元素,地址加4,可见只要知道了数组的首地址,就知道了数组中每一个元素的地址,所以利用数组下标可以实现数组元素的随机访问。
随机访问寻址方法:a[i]_address = base_address + i * data_type_size
其中,base_address 为数组首地址,data_type_size 为每个元素的大小,依数据类型确定。
代码验证一下:
#include
int main()
{
int a[] = {0, 1, 2, 3};
printf(" a.address: %p\n", a);
for (int i = 0; i < 4; i++)
{
printf("a[%d].address: %p\n", i, &a[i]);
}
}
打印结果为:
a.address: 0x7ffd46f51c50
a[0].address: 0x7ffd46f51c50
a[1].address: 0x7ffd46f51c54
a[2].address: 0x7ffd46f51c58
a[3].address: 0x7ffd46f51c5c
对于二维数组(多维数组),依然是线性存储的,寻址方式依然是通过元素下标。
二维数组寻址:对于 m * n 的二维数组, a [ i ] [ j ] ( i < m , j < n ) a [ i ][ j ] (i < m, j < n) a[i][j](i<m,j<n)的地址为:
a[i][j]_address = base_address + ( i * n + j) * data_type_size
这里注意,查找和随机访问是不一样的,随机访问是给出索引(位置)返回数据元素的值,而查找一般是给出数据元素的值返回它的索引(位置)。
所以,数组查找操作的时间复杂度不是O(1),要看使用的查找算法,比如二分查找,时间复杂度为 O(logn)。
数组申请的内存是有限的,如果访问的内存不属于数组,就会发生数组越界问题。
int main(int argc, char* argv[])
{
int i = 0;
int arr[3] = {0};
for(; i<=3; i++)
{
arr[i] = 0;
printf("hello world!\n");
}
return 0;
}
这一段程序数组越界了,输出结果和内存地址分配方式有关。
函数体内的局部变量存在栈上,且是连续压栈。在Linux进程的内存布局中,栈区在高地址空间,从高向低增长。变量i和arr在相邻地址,在内存分配时,如果按照内存地址递减的方式进行分配,则i比arr的地址大,那么arr越界正好访问到i。当然,前提是i和arr元素同类型,否则那段代码仍是未决行为。这时无限循环输出hello world。
如果按照内存地址递增的方式进行分配,则i比arr的地址小,那么arr越界正好访问不到i,这时只输出4次hello world。
从数组存储的内存模型上来看,下标最准确的定义应该是偏移(offset)。第一个元素的偏移为0,第i个元素的偏移为i。但如果从1开始,第i个元素的偏移就为i-1,多了一次减法运算,这将降低CPU的运算效率。(对于底层算法开发,效率优化要做到极致。)