数组提到数组,相信大家都不陌生,毕竟每一种编程语言都会有它的影子。数组是最基本的数据结构。虽然数组看起来很基础很简单,但是要掌握这个基础数据结构的本质却不是那么简单。开门见山数组(Array)是一种线性表数据结构,它使用一组连续的内存空间来存储一组相同类型的数据。这个定义有几个关键字,是数组的精髓。让我们从这些关键字进一步了解数组。第一个是线性表。顾名思义,线性表的特点是数据像直线一样排列的结构。每个线性表的数据最多有前后两个方向。除了数组之外,链表、队列、栈等数据结构也是线性表结构。例如,糖葫芦字符串与线性表非常相似。将糖葫芦(数据)串成一条直线串在竹签上,每只糖葫芦(数据)最多有前后两个方向。二是连续的内存空间和同一类型的数据。由于这两个条件的限制,数组有一个很重要的特性:随机访问元素,随机访问元素的时间复杂度为O(1)。但有优点也有缺点。这两个条件的限制导致在插入或删除一条数据时需要移动数据,以保证数据的连续性。随机存取数组是如何根据下表实现对数组元素的随机存取的?我们以一个长度为5的int类型的数组inta[5]为例。当我们定义这个数组时,计算机会为数组inta[5]分配一块连续的内存空间。假设,数组inta[5]内存块的首地址为base_address=100,则a[0]的地址为100(首地址)a[2]的地址为104a[3]为108a[3]地址为112a[4],地址为116,计算机通过访问内存地址来访问内存中存储的数据。那么当计算机要随机访问数组中的某个元素时,就会通过下面的寻址公式计算出对应元素的内存地址,从而通过内存地址访问数据。a[i]_address=base_address+i*data_type_sizea[i]_address表示对应数组下标的内存地址,data_type_size表示数组存放的数据类型的大小,数组inta[5]。它存储了5个int类型的数据,它的data_type_size是4个字节。二维数组的寻址公式,假设二维数组的维数为mn,公式为:a[i][j]_address=base_address+(i*n+j)*data_type_size为什么数组下标从0开始?先回答这道题的时候,我们假设数组下标从1开始,a[1]代表数组首地址,那么计算机的寻址公式就会变成:a[i]_address=base_address+(i-1)*data_type_size对比数组下标从0开始和数组下标从1开始的寻址公式,我们不难看出,从1开始,每次随机访问数组元素都需要进行减法运算。对于CPU来说,也就是多一条减法指令。更何况数组是一个非常基础的数据结构,使用频率非常高,效率优化必须做到极致。因此,为了减少CPU的一条减法指令,数组选择从0开始编号,而不是从1开始编号。以上是从计算机寻址公式的角度分析的。当然,还有历史原因。数组的插入和删除过程前面说过,对于数组的定义,为了保持内存数据的连续性,插入和删除这两个操作的效率会比较低。接下来解释一下为什么会通过代码导致效率低下?改善方法有哪些?插入操作过程数据的不同场景、不同的插入位置,插入操作的时间复杂度略有不同。下面分别分析数组中数据有序和不规则两种场景下的插入操作。不管是什么场景,如果在数组末尾插入元素,就很简单了。不需要移动数据,直接把元素放在数组的末尾即可。此时空间复杂度为O(1)。在数组的开头或中间插入数据怎么样?这个时候可以根据不同的场景采用不同的方法。如果数组中的数据是有序的(从小到大或从大到小),当在第k个位置插入新元素时,k之后的数据必须向后移一位,此时最坏的时间复杂度为在)。如果数组中的数据没有规则,那么在第k个位置插入新元素时,先将旧的第k个位置数据移动到数据末尾,然后将新元素数据直接放入第k个位置。那么在这个具体场景下,在第k个位置插入一个元素的时间复杂度是O(1)。一图胜千言,我们以图形的形式展示了在有序和不规则的场景中插入元素的过程。删除操作过程与插入数据类似。如果我们要删除第k个位置的数据,为了内存的连续性,我们还需要移动数据。否则,中间就会出现一个洞,记忆就无法连续了。如果删除数组末尾的数据,时间复杂度为O(1);如果删除开头的数据,位置k之后的数据需要向前移动一位,那么时间复杂度为O(n)。一图胜千言,我们以图片的形式展示数组删除操作。代码实战数组的插入、删除和查询本例中,数组中的数据是有序的(数据按照从小到大的顺序),实现了对数组的插入、删除和查询操作。先用结构体定义数组的属性,包括数组的长度、占用的个数和数组指针。结构Array_t{int长度;//数组长度int使用;//占用的个数int*arr;//数组地址};创建数组:根据结构体设置的数组长度,创建相应的连续空间且类型相同的Arrayvoidalloc(structArray_t*array){array->arr=(int*)malloc(array->length*sizeof(int)));}插入过程:判断数组占用个数是否超过数组长度遍历数组,找到插入新元素的下标idx。如果发现插入元素的下标不是结束位置,需要将idx数据一个一个向后移动,在idx下标处插入新元素,加上+1/**占用的数组个数插入新元素。元素*参数1:Array_t数组结构指针*参数2:新元素的值*返回:成功返回插入的数组下标,失败返回-1*/intinsertElem(structArray_t*array,intelem){//当占用的数组个数大于等于数组长度时,表示数组的所有下标都存储了数据,无法插入if(array->used>=array->length){std::cout<<"错误:数组大小已满,无法插入"<
