数据结构和算法-数组

108 阅读2分钟

定义

数组是一种线性表数据结构,它使用连续的内存空间,存储相同类型的数据。

算法

随机访问

设随机访问下标为i,数组的首地址为base,数组每个数据单元占用字节为k,那么有

//数组下标从0开始
addr = base + i * k
  • 时间复杂度:O(1)
  • 空间复杂度:O(1)

查找元素

在数组中查找一个元素,需要从头开始遍历

func find(nums int[], target int) int {
    for i:=0; i<len(nums); i++ {
        if nums[i] == target {
            return i
        }
    }
    return -1
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

插入元素

(数组空间足够)往数组插入一个元素,需要搬迁数据

func insert(nums int[], int ti, element int) {
    for i:=len(nums); i>ti; i-- {
        nums[i] = nums[i-1]
    }
    nums[ti] = element
    //len(nums) += 1
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

删除元素

func remove(nums int[], int di) {
    for i:=di; i<len(nums); i++ {
        nums[i] = nums[i+1]
    }
    //len(nums) -= 1
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

其他算法

作用在数组上的算法还有很多,比较典型的当属排序和二分查找了,后续独立文章讲解。

优点和缺点

优点

空间连续,所以有以下两个优点

  • 支持随机访问
  • 可以配合CPU预读特性实现更高的访问性能

缺点

  • 插入/删除慢,需要搬迁数据
  • 空间大小固定,需要手动扩容

适用场景

以数组的优缺点为指导思想,具体场景具体分析。

拓展问题

为什么很多编程语言数组的下标从0开始

假设数组下标从1开始,那么随机访问地址的计算公式为

//数组下标从1开始
addr = base + (i-1) * k

相比于从0开始,每次要多计算一次i-1;换句话说,下标从0开始,运算指令更少,可以提高随机访问的性能。

往数组插入元素时,如果空间不足怎么办?

数组申请时即确定了长度,往一个已经被完全使用的数组中插入元素,需要手动扩容,否则会发生数组越界问题或者数据丢失(看具体实现)。作为学习,我们可以对数组进行封装,使其支持自动扩容;实际开发中,我们可以使用现成的库,很多编程语言都提供了支持自动扩容的数组,比如Java的ArrayList