动态数组
动态数组 | |
---|---|
术语名称 | 动态数组 |
英语名称 | dynamic array |
别名 | 变长数组, resizable array, growable array, array list |
动态数组(dynamic array)或变长数组(resizable array)指逻辑结构为线性表、存储结构为数组,且数组长度可变的数据结构。也就是长度可变的顺序表。
动态数组通常涉及内存的动态分配、移动和动态释放,因此被称为动态数组。 本质上,动态数组和静态数组的区分是其是否涉及运行期的(动态)内存分配释放操作。 即使长度不进行变更,如果语言允许数组长度编译时未知,在运行期重新分配,这一数组也视为不太典型的动态数组; 相反,如果编译时预设最大长度,运行期修改其使用范围,则视为不太典型的静态数组。
语言实例
动态数组 | |||
---|---|---|---|
语言 | 版本/库 | 对应内容 | 说明 |
C | C89 | 包括三种:
|
标准库及语法规则 |
C++ | C++98 | std::vector
|
STL |
实现实例
libcxx: libc/src/__support/CPP/array.h
libstdc++: libstdc++-v3/include/std/array
GSSDS 定义
GSSDS: array/include/sds/dynamic_array/dynamic_array.h
时间复杂度估计与优化
本节讨论动态数组对其逻辑结构线性表的操作实现。
插入删除操作
由于需要保持索引与地址的公式关系,元素的插入和删除需要较复杂的操作,见数组的插入、删除。
重新分配操作
动态数组的一个常见使用方式是逐渐加入新元素,如果每次扩展 [math]\displaystyle{ \Delta s }[/math] 的大小,此时添加 [math]\displaystyle{ n }[/math] 次需要添加 [math]\displaystyle{ \tfrac{n}{\Delta s} }[/math] 次重新的内存分配。如果每次以不大于 2 的比例增加,则这个复杂度可以被平摊到线性。这一比例称为增长因子(growth factor)。在不同的实现中,增长因子可能为 1.5 (如 MSVC , Java ),也可能为 2 (如 gcc libcxx , clang stdlibc++)[1][2] 。
时间复杂度
记数组长度为 [math]\displaystyle{ n }[/math] 。
操作 | 最好 | 最坏 | 平均 | |
---|---|---|---|---|
基本操作 | 初始化 Create
|
一次分配,可能和是否指定预计长度相关 | ||
长度 Length/Size
|
[math]\displaystyle{ O(1) }[/math] ,用保存的长度或指针差 | |||
清空 | 一次释放,可能和长度有关 | |||
元素获取 | 指定索引、指定位置 Get
|
[math]\displaystyle{ O(1) }[/math] ,只需要计算地址偏移 | ||
头部 Head
| ||||
尾部 Rear
| ||||
插入删除 | 动态内存分配附加 | 平摊 [math]\displaystyle{ O(n) }[/math] ,每次使用增长因子扩展 | ||
指定索引、指定位置(无重新分配) Insert/Remove
|
[math]\displaystyle{ \Theta(1) }[/math] ,尾部 | [math]\displaystyle{ \Theta(n) }[/math] ,头部 | [math]\displaystyle{ \sim\tfrac{n}{2}=\Theta(n) }[/math] ,平均后续长度 | |
头部(无重新分配) | [math]\displaystyle{ \Theta(n) }[/math] | |||
尾部(无重新分配) | [math]\displaystyle{ \Theta(1) }[/math] | |||
元素顺序 | 指定位置前趋后继 Pred/Succ
|
[math]\displaystyle{ \Theta(1) }[/math] ,只需要计算地址偏移 |
- ↑ https://www.zhihu.com/question/36538542 C++ STL 中 vector 内存用尽后,为啥每次是两倍的增长,而不是3倍或其他数值? - 知乎]
- ↑ https://en.wikipedia.org/wiki/Dynamic_array#Growth_factor