前言:
在大一刚接触C语言的数组时,发现第一项的下标是0,第二项的下标是1,以此类推....那个时候觉得非常别扭,不懂为什么,只能记住和就这样使用。后来发现,数组在大多数编程语言中都有,是一种常见的数据类型,并且下标都是如此。
什么是数组?
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。(ps:在js中,可以存储不同类型的数据)
什么是线性表?
简单来说,就是数据排成像一条线一样的结构,那么,这种结构最多有前和后两个结构
常见的线性表数据结构有哪些:
数组、栈、队列、链表(这里我先不对这几种结构做过多的介绍与解释)
什么是非线性表?
与线性表相对,就是数据结构不像一条线那样的结构,不是简单的前后关系
常见的非线性表数据结构有哪些:
二叉树、图、堆(这里我也不对这几种结构做过多的介绍与解释)
连续的内存空间:
由于数组具有连续的内存空间,所以数组有了随机访问这个强大的特性,但是也存在弊端,会让我们在操作数组的时候变得非常低效,比如我们在做插入、删除的时候,就需要做大量的数据搬移操作。
数组和链表的区别:
数组适合查找,它支持随机访问,根据下标随机访问的时间复杂度为 O(1)
链表适合插入、删除,时间复杂度 O(1)
分析低效的“插入”和“删除”:
插入:
假设现在有一个数组,长度为n,我现在要对它进行插入操作,我要在它的第m个位置插入一个数据,那么,我需要为这个新数据腾出空间,即我要把第m~n之间的数据向后移动一位,所以时间复杂度为 O(n);
但是,如果我在数组的结尾插入数据,那么其他的数据都不需要移动的,这是最好的情况,时间复杂度为 O(1);
如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。在这种情况下,如果要将某个数组插入到第 m个位置,为了避免大规模的数据搬移,我们还有一个简单的办法就是,直接将第 m 位的数据搬移到数组元素的最后,把新的元素直接放入第 m 个位置。
总结:利用这种思想,在某些特定的情况下,我们可以将在第k个位置的插入操作,时间复杂度降为 O(1);
删除:
删除和插入类似,如果是在结尾删除,时间复杂度为 O(1);若是在开头,则为 O(n);
在日常的一些场景下,我们可能不止删除一个数据,假设我们需要删除m个元素,为了避免被删除的元素的后面的元素移动m次,我们可以先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。
这就好比我们在日常生活中往垃圾桶里丢垃圾,生活中,我们扔进垃圾桶的垃圾, 并没有消失,只是被 ''标记'' 成了垃圾, 只有垃圾桶塞满时,才会清理垃圾桶。
解释下标为什么从0开始:

计算机会给每个内存单元分配一个地址,计算机通过地址来访问内存中的数据。当计算机需要随机访问数组中的某个元素时,它会首先通过下面的寻址公式,计算出该元素存储的内存地址:
假设base = 1000;每个空间存储的是数据类型,所以:
下标从0开始:
a[i]base = base + i * 4;
如果下标从1开始:
a[i]base = base + (i-1) * 4;
可见,与上面的公式相比,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。所以我们的下标是从0开始的。