数组介绍
一、什么是数组
数组是一种线性表数据结构。他用一组连续的内存空间,来存储一组具有相同类型的数据。
注意几个关键词
-
线性表
什么是线性表?
线性表是一种逻辑结构,是相同类型数据的有限序列,除第一个元素之外有且只有一个前驱,除最后一个元素之外,每个元素有且只有一个直接后继。
线性表的特点:-
元素的个数是有限的
-
逻辑上元素有先后顺序
-
数据类型相同
-
仅讨论元素间的逻辑关系
-
与线性表对应的为非线性表,如二叉树,堆,图等,他们的数据并不是简单的前后关系
-
连续的空间以及相同的数据结构
数据的随机访问特性靠这两个来保证,因为是连续的空间,所以我可以根据下表去随机访问数据的每个元素。 -
那么数组的下标为什么会设置成从0开始呢?
数据地址的计算其实是有个公式:
address = base_address + i * datatype_size
address 为数组的地址
base_address为为数组分配空间时,起始地址
i代表数组的下标
datatype_size代表数据类型的大小
new 一个int[10]的数组,假设起始地址为1000,则地址分配如下图所示
二、数组的查找与插入、删除
-
查找
我们常说的数组查找的效率是O(1),这边的查找是指根据数组下标去随机访问某一个数组元素的时间复杂度是O(1),假如你遍历数组去查询某个元素,不是根据下标去随机访问,那么他的时间复杂度其实是O(logn)。 -
插入
为了保证数组的连续性,假设数组的最末尾的元素后面还有空间,我们直接在最后面插入,那么时间复杂度为O(1),但是假如我要在第一位插入,那么相应的数据每个元素都要往后面移动一位,此时时间复杂度是O(n)。这边能直接在数组的第一位前面插入么?肯定是不能的,上面已经介绍过数组寻址的公式,数组的起始地址已经分配好了,所以我们没有办法在数组的前面插入。插入的最小时间复杂度是O(1),最大时间复杂度是O(n),那么平均时间复杂度就是(1+2+…+n)/n,即为O(n)。
如果数组是有序的,那么我们每次插入必须都得这样去移动数组元素,但是假如数组是无序的,我们可以怎么操作呢。比如我new一个数组int[] a = new int[10],我在a[0]到a[5]之间存储了相应的元素,那么我要在a[2]插入一个元素,我可以直接将a[2]=我要插入的值,将原来a[2]的值放到最后,即a[6]=a[2]。如下图所示
-
删除
删除与插入同理,每次删除都要移动数组元素,来保证数据的连续性,那么时间复杂度也为O(n)。但是在实际场景中,我们并不一定要每次删除都立即清除,我们可以每次删除都做一个标记,等到内存空间不够的时候再去统一删除,这样效率会比较高一点。事实上JVM的标记清除算法就是这种思想
数组的优缺点
-
数组的优点:
随机访问性强
查找速度快 -
数组的缺点:
插入和删除效率低下
数组的大小固定,不能动态扩展,可能存在内存空间浪费
数组的内存空间要求较高,必须是连续的内存空间