数组

数组与链表的区别

随机访问是数组与链表最本质的区别,而“数组适合查找,链表适合插入和删除”这种表述并不准确。数组查找的时间复杂度并不为O(1),即使排好序的数组,使用二分查找,时间复杂度也是O(logn)。数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)

数组特性

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

线性表

每个线性表上的数据最多只有前和后两个方向

非线性表数据之间关系就会复杂很多,比如二叉树,堆,图。

连续的内存空间和相同类型的数据

随机访问正是因为这两个特性才能实现。这也导致删除和插入的时候,需要做大量数据搬移的工作。

如何根据下标随机访问数组元素

假设有有一个数组 int[] a = new int[10],内存块首地址为base_address = 1000.

根据寻址公式来计算该元素存储的内存地址:(data_type_size 表示数组中每个元素的大小)

a[i]_address = base_address + i * data_type_size

数组的插入

数组插入的平均时间复杂度为O(n),因为最坏情况需要移动所有的元素。

想要降低复杂度可以将要插入位置的旧元素挪到数组尾部,然后将新元素插入即可。

数组的删除

删除元素后一样会伴随搬移数据,为了保证内存的连续性。平均时间复杂度也为O(n).

特殊场景下可以把删除操作集中到一起执行,也就是每次删除操作并不是真正搬移数据,只是记录数据已经被删除。当数组没有更多空间存储时,再触发一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。JVM的标记清除算法就是如此。

容器与数组

容器(例如ArrayList)的最大优势就是可以将很多数组操作的细节封装起来,另外就是支持动态扩容,ArrayList不设默认长度时候,则先默认设置10的内存空间,之后如果长度大于10则扩容1.5倍,同时复制数组。

  1. 容器无法储存基本类型,如果要使用基本类型可以使用数组
  2. 底层开发可以为了性能考虑可以使用数组。

数组下标为什么从0开始

之前的寻址公式为:

a[k]_address = base_address + k * type_size

如果数组下标是从1开始,那么寻址公式就会变为:

a[k]_address = base_address + (k - 1) * type_size

每次随机访问元素都多了一次减法运算,对于CPU来说,就多了一次减法指令。

posted @ 2020-07-05 21:35  六月的余晖  阅读(138)  评论(0编辑  收藏  举报