要求:元素的查找、删除、插入
数组是一种将相同类型元素存储在连续内存空间的线性表数据结构,数组中的位置,称为索引。
线性表,即数据排列像一条线一样的结构。从对数据进行操作的方向来理解,每个线性表上的数据最多只有前、后两个方向。与线性表相对立的概念是非线性表,即数据的方向不是简单的前后关系。
因为相同类型元素和连续内存空间的限制,所以数组查找元素非常高效。 通过如下寻址公式,直接计算出指定索引的内存地址,通过内存地址,拿到数组元素:
array_address + i * data_type_size = value_address 数组内存地址 + 索引 * 元素类型的长度 = 元素内存地址
可以将索引理解为偏移量。
64位操作系统下,在 Go 中元素类型的长度占用字节对照表:
记忆技巧,一个数值类型名称中的数字,表示每个这个类型的值在内存中占多少比特
插入或删除数组中的元素效率是低下的,这是数组的缺点。假设想要在数组某位置插入一个元素,由于数组在内存中是”紧凑“的,它们之间没有任何空间放数据。因此我们不得不将插入位置此索引之后的所有元素都先向后移动一位,再将元素放进去。删除元素也是类似的。
实际上,在某些特殊场景下,如果我们将多次删除操作集中在一起执行,删除的效率是不是会高很多呢?我们可以先记录已经删除的数据,但并没有真正的搬移。当数组没有更多空间存储数据时,我们再触发一次真正的删除操作。
数组的常见操作
在 Go 中,指定长度时([5]int)为数组,不指定长度时([]int)为切片。由于 Go 的数组被设计为在编译期确定长度,因此只能使用常量来指定长度。为了方便演示常见操作,约定将切片看做数组。
数组遍历
// 遍历数组
func traverse(nums []int) {
count := 0
// 通过索引遍历数组
for i := 0; i < len(nums); i++ {
count++
}
}
数组查找
// 在数组中查找指定元素
func find(nums []int, target int) (index int) {
index = -1
for i := 0; i < len(nums); i++ {
if nums[i] == target {
index = i
break
}
}
return
}
数组删除
// 删除索引 index 处元素
func remove(nums []int, index int) {
// 把索引 index 之后的所有元素向前移动一位
for i := index; i < len(nums)-1; i++ {
nums[i] = nums[i+1]
}
}
数组插入
// 在数组的索引 index 处插入元素 numfunc insert(nums []int, num int, index int) {
// 把索引 index 以及之后的所有元素向后移动一位
for i := len(nums) - 1; i > index; i-- {
nums[i] = nums[i-1]
}
// 将 num 赋给 index 处元素
nums[index] = num
}
其他
为什么数组元素索引从 0 开始编号? 根据地址计算公式,索引本质上表示的是内存地址偏移量,首个元素的地址偏移量是 0 ,所以索引是 0 也就很自然了。