1145 字
6 分钟
260715GO数组
数组(顺序存储)基本原理
一、数组两大分类
- 静态数组:原始形态,是一块固定长度、连续的内存空间,依靠索引直接访问内存。长度初始化后不可改变。
- 动态数组:基于静态数组封装而来,内置
push、insert、remove等常用 API,可自动完成扩容/缩容,简化增删操作。- 后续栈、队列、哈希表等很多数据结构底层都依赖动态数组实现
- 算法刷题和开发日常一般直接使用动态数组(Go 的 slice),静态数组多用于理解底层原理
Go 静态数组定义
// 定义长度固定为 10 的 int 静态数组var arr [10]int// 索引赋值arr[0] = 1arr[1] = 2// 索引取值a := arr[0]- 核心特性:随机访问 — 通过索引可 O(1) 时间直接读取/修改对应元素
二、静态数组:增删原理 & 时间复杂度
1. 新增元素
情况1:末尾追加(有空闲空间)
直接对末尾空位索引赋值,无需移动元素
var arr [10]int// 前4个位置存入元素for i := 0; i < 4; i++ { arr[i] = i}// 末尾追加元素arr[4] = 4arr[5] = 5- 时间复杂度:O(1)
情况2:中间插入(有空闲空间)
需要把插入位置及之后的原有元素向后整体搬移一位(倒序遍历防止数据覆盖),腾出空位后插入新元素
package mainimport "fmt"
func main() { var arr [10]int for i := 0; i < 4; i++ { arr[i] = i } // 在索引 2 插入元素 666,后移后续元素 for i := 4; i > 2; i-- { arr[i] = arr[i-1] } arr[2] = 666 fmt.Println(arr)}- 时间复杂度:O(N)
情况3:数组空间已满
需要手动扩容:新建一块更大的静态数组,复制原有全部数据,再进行新增操作
// 原数组已满arr := make([]int, 10)for i := 0; i < 10; i++ { arr[i] = i}// 创建更大数组并复制原数据newArr := make([]int, 20)for i := 0; i < 10; i++ { newArr[i] = arr[i]}// 在新数组末尾追加元素newArr[10] = 10- 扩容涉及全量拷贝,单次扩容操作复杂度:O(N)
2. 删除元素
情况1:删除末尾元素
直接标记/截断末尾元素,无需搬移数据
var arr [10]intfor i := 0; i < 5; i++ { arr[i] = i}// 删除末尾元素(示例标记为-1)arr[4] = -1- 时间复杂度:O(1)
情况2:删除中间元素
把被删元素后面的元素整体向前搬移一位(正序遍历),保持数组连续性
var arr [10]intfor i := 0; i < 5; i++ { arr[i] = i}// 删除索引1的元素,后续元素前移for i := 1; i < 4; i++ { arr[i] = arr[i + 1]}arr[4] = -1- 时间复杂度:O(N)
3. 查 & 改
- 按索引查询元素:O(1)
- 按索引修改元素:O(1)
- 按元素值遍历查找(非索引查找):O(N)
4. 静态数组复杂度总览
- 增
- 末尾追加(有空位):O(1)
- 中间插入:O(N)
- 满数组扩容新增:O(N)
- 删
- 删除末尾元素:O(1)
- 删除中间元素:O(N)
- 查(索引访问):O(1)
- 改(索引修改):O(1)
三、动态数组(Go Slice)用法
底层依旧依托静态数组,自动处理扩容、缩容和数据搬移,对外提供简洁 API
package mainimport "fmt"
func main() { // 创建初始长度为0的动态数组 arr := make([]int, 0)
// 末尾追加元素 for i := 0; i < 10; i++ { arr = append(arr, i) }
// 在索引 2 插入元素 666 arr = append(arr[:2], append([]int{666}, arr[2:]...)...)
// 在头部插入元素 -1 arr = append([]int{-1}, arr...)
// 删除末尾元素 arr = arr[:len(arr)-1]
// 删除索引 2 的元素 arr = append(arr[:2], arr[3:]...)
// 索引查询 a := arr[0] fmt.Println(a)
// 索引修改 arr[0] = 100
// 根据值查找索引(遍历查找,复杂度 O(N)) index := -1 for i, v := range arr { if v == 666 { index = i break } } fmt.Println("666的索引:", index)}四、核心特点总结
- ✅ 优点:支持索引随机访问,末尾增删效率极高(O(1)),结构简单
- ❌ 缺点:中间位置增删效率差(O(N)),会产生大量数据搬移;静态数组长度固定不够灵活
- 底层本质:连续内存存储,动态数组 = 静态数组 + 自动扩容/缩容 + 封装增删API
- 适用场景:读多写少、主要在尾部增删、频繁按索引随机访问的场景;不适合频繁在数组中间插入删除的场景