数组(Array)

数组可以说是一种非常基础的数据结构。看起来非常的简单,其实还有很多的门道。

数组是一种线性表结构,一个固定长度的存储的数据结构,数组中的元素被存储在一段连续的内存空间中。

线性表结构:就是数据是排列成一排的像一条线一样的结构。这类结构是指存在前后两个方向的。在数据结构中,除了数组,还有链表、队列、栈等也是线性表。

非线性表结构:代表数据不仅有前后两个关系,比如二叉树、堆、图。

连续的内存空间和相同的类型的数据。正式因为这个特点,数组才能够实现一个随机访问,我们可以以索引的方式访问数组的数据元素。

声明数组的语法。

JavaScript 语言中声明数组的语法如下:

arr = [1,2,3];
arr = new Array(100);

数组的遍历

arr = [1,2,3];

for (var i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

随机访问:有利就有弊。这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。

那么数组是如何实现下标访问的?

var arr = new Array(10); 一个长度为 10 的数组,计算机给数组 arr 分配了一块连续连续的内存空间,内存的首地址为 base_address = 1000,在存储数组的时候,仅存第一个元素的地址,也就是 base_address,数组其它元素的地址可以通过对基地址的偏移来计算。

使用公式来计算数组元素的内存地址。

一维数组的内存地址。

A[i]_address = base_address(A) + i * date_type_size

date_type_size表示数组中每个元素的大小。在 JavaScript 中 date_type_size 就表示2个字节。

二维数组的内存地址。

a[i][j] (i<m, j <n)的地址:address = base_address + (i * n + j) * type_size

数组支持随机访问,根据下标访问的时间复杂度为 O(1)。但是,注意:数组适合查找操作,但是查找的时间复杂度 最好为O(1),最坏时间复杂度为 O(n),平均时间复杂度为O(n),我们一般都是说为平均时间复杂度O(n),即便使用二分查找,时间复杂度都为O(logn), 根据下标访问的时间复杂度为 O(1)。

低效的“插入”和“删除”

数组为了保持内存数据的连续性,会导致插入、删除这两个操作比较低效。

插入操作。

我们假设数组的长度为 n,当我们随机插入一个元素,最好的情况就是直接在末尾,数组的长度位置插入,时间复杂度为 O(1),最坏的复杂度是在数组的第一个元素位置插入,时间复杂度为O(n),代表所有的元素都要向后移动一位,这样的操作是很耗时的。因为每个位置的插入元素的概率是一样的,所以时间复杂度为 (1+2+...+n)/n = O(n)。

如果在数组的存储的数据是有序的,那么我们就必须遵守上面的移动,但是如果是无序的,数组只是看作一个存储数据的集合。那么我们可以使用搬迁,也就是将需要插入的位置插入一个元素,然后将之前位置上的元素放到元素的末尾,这样插入的时间复杂度将为O(1)。这个处理思想在快排中也会用到。

删除操作。

跟插入数据类似,如果我们要删除某个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞,内存就不连续了。和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。

但是,很某些情况下,我们可以不用考虑连续性问题,如果我们将多次删除操作集中在一起执行,删除的效率是不是会提高很多呢?

我们可以先记录删除的元素,并不移动数组,不是真正上的删除,知识记录即可,待到多次之后,可以执行一次性的删除操作。这样就大大减少了删除操作导致的数据搬移。

空间要求

数组仅占据用于存储指定数据类型的元素的空间,这就意味着,存储n个元素所要求的空间复杂度为O(n)

为什么数组是从 0 开始,而不是我们所认为的 1 开始。

我们可以将数组的下标确切的定义为 偏移量。如果用 a 来表示数组的首地址,a[0]就是偏移为 0 的位置,也就是首地址,a[k]就表示偏移 k 个 type_size 的位置,所以计算 a[k]的内存地址只需要用这个公式:

a[k]_address = base_address + k * type_size

但是,如果数组从 1 开始计数,那我们计算数组元素 a[k]的内存地址就会变为:

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

从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。

好多编程语言的数组计数为什么从零开始 1 底层计算机寻址指令可以少计算一个减法 2,最主要的还是因为历史原因。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值