数据结构与算法 - 数组
数据结构与算法 - 数组
数据结构
一、定义
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
线性表(Linear List)就是数据排成像一条线一样的结构。每个线性表上的数据最多只有两个方向。除了数组,链表、队列、栈也是线性表结构。
与线性表对立的是非线性表,比如二叉树、堆、图等。之所以叫非线性,是因为,在非线性表中,数据之间并不是简单的前后关系。
二、操作
| 操作 | 时间复杂度 |
|---|---|
| 查找 | \(O(1)\) |
| 插入 | 开头\(O(1)\),末尾\(O(n)\),平均\(O(n)\) |
| 删除 | 开头\(O(1)\),末尾\(O(n)\),平均\(O(n)\) |
三、细节
- 为什么数据编号从0开始?
从数组的存储模型上来看,“下标”最确切的定义应该是“偏移量(offset)”。
如果用a代表数组的首地址,a[0]就是偏移量为0的位置,也就是首地址,a[k]就表示偏移量k个 type_size 的位置,所以计算 a[k] 的内存地址,以1开始,则多一次减法运算:
- 数组类型与大小如何选择?
- 数组类型根据实际的情况,预先估算数据量大小、存储大小等需求;
- 如果数据大小事先已知,并且对数据的操作非常简单,可以定义静态数组;
- 如果数据大小需要动态添加,并且需要反复操作,可以定义动态数组
vector;
静态与动态数组
一、 定义
1.静态数组:不可以更改数组长度的
2.动态数组:动态数组本质上就是数组,是由静态数组封装的一些扩容能力。
静态数组在内存中位于栈区,是在定义时就已经在栈上分配了固定大小,在运行时这个大小不能改变,在函数执行完以后,系统自动销毁;
动态数组是用户自己创建出来的,位于内存的堆区,它的大小是在运行时给定,并且可以改变其大小。同时,使用完必须由程序员自己释放,否则严重会引起内存泄露。
二、 动态扩容机制
vector(c++):面试题:C++vector的动态扩容,为何是1.5倍或者是2倍
-
扩容原理?当向vector中插入元素时,如果元素有效个数size与空间容量capacity相等时,vector内部会触发扩容机制。拷贝元素和释放旧空间,可以通过&vector[0]的方式来查看数据首地址改变情况。
-
扩容大小?每次扩容新空间不能太大,也不能太小,太大容易造成空间浪费,太小则会导致频繁扩容而影响程序效率。不同的的编译器实现方式不同,vs中以1.5倍扩容,GCC以2倍扩容。
-
如何避免扩容导致效率低?如果在插入之前,可以预估vector存储元素的个数,提前将底层容量开辟好即可。如果插入之前进行reserve,只要空间给足,则插入时不会扩容,如果没有reserve,则会边插入边扩容,效率极其低下。
-
为什么选择以倍数方式扩容?以等长个数k进行扩容,向vector插入n个元素,需要插入元素操作和搬移元素操作的总和:\(n + \sum_{i=1}^{\frac{n}{k}}ik= n + \frac{(1+n/k)*n}{2}\),平摊下来每次操作时间\((n + \frac{(1+n/k)*n}{2})/n=3/2+n/2*k = O(N)\)。以倍数方式m进行扩容,向vector插入n个元素,需要插入元素操作和搬移元素操作的总和:\(n + \sum_{i=1}^{\log_{m}{n}}m^i= n + \frac{m(n-1)}{m-1}=n+\frac{mn}{m-1}\),平摊每次操作的时间\((n+\frac{mn}{m-1})/n = O(\frac{m}{m-1}))=O(1)\),m为常量。
-
为什么选择1.5倍或者2倍方式扩容,而不是3倍、4倍?(斐波那契数,1.618)理想的分配方案是在第N次扩容时如果能复用之前N-1次释放的空间,因此按照小于2倍方式扩容,多次扩容之后就可以复用之前释放空间。如果倍数超过2倍(包含2倍)方式扩容会存在:(1)空间浪费可能会比较高,比如:扩容后申请了64个空间,但只存了33个元素,有接近一半的空间没有使用。(2)无法使用到前面已释放的内存。
List(python):list是以两倍进行扩容,并且会创建新的数组,然后拷贝,系统再回收久数组。
二维数组
一、定义
二维数组就是在一维数组上,多加一个维度;
二、声明与初始化
- c++ (静态数组)
//数据类型 数组名[行数][列数];
int a1[3][3];
//数据类型 数组名[行数][列数] = {{数据1,数据2,数据3},{数据4,数据5}};
int a2[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
//数据类型 数组名[行数][列数] ={数据1,数据2,数据3,数据4}
int a2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
//数据类型 数组名[ ] [列数] = {数据1,数据2,数据3,数据4};
int a3[][3] = { 1,2,3,4,5,6 };
//查询二维数组所占内存空间
cout << "二维数组占用内存空间为:" << sizeof(arr) << endl;
cout << "二维数组第一行占用内存为:" << sizeof(arr[0]) << endl;
cout << "二维数组第一个元素所占内存空间" << sizeof(arr[0][0]) << endl;
// 行列数
cout << "二维数组行数为:" << sizeof(arr) / sizeof(arr[0]) << endl;
cout << "二维数组列为:" << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
//可以查看二维数组的首地址
cout << "二维数组首地址为:" << (int)arr << endl;
cout << "二维数组第一行首地址为:" << (int)arr[0] << endl;
cout << "二维数组第二行首地址为:" << (int)arr[1] << endl;
cout << "二维数组第一个元素首地址:" << (int)&arr[0][0] << endl;
- c++ (动态数组)
vector<vector<int>> a(m, vecotr<int>(n, 0));
- python (list)
li = [[0 for i in range(m)] for j in range(n)]
山脉数组
一、 定义
山脉数组:\(arr.length >= 3\),在 \(0 < i < arr.length - 1\)条件下,存在\(i\)使得:
- \(arr[0] < arr[1] < ... arr[i-1] < arr[i]\)
- \(arr[i] > arr[i+1] > ... > arr[arr.length - 1]\)
二、 题型
| 序号 | 题目 | 难度 |
|---|---|---|
| —— | ———————————————————————————————————————————————— | ————— |
| 0845 | 845. 数组中的最长山脉 | 中等 |
| 0852 | 852. 山脉数组的峰顶索引 | 简单 |
| 0941 | 941. 有效的山脉数组 | 简单 |
| 1095 | 1095. 山脉数组中查找目标值 | 困难 |
旋转数组
一、 定义
旋转数组:nums在预先未知的某个下标 \(k(0 <= k < nums.length)\)上进行了 旋转,使数组变为\([nums[k], ..., nums[n-1], nums[0], ..., nums[k-1]]\)(下标 从 0 开始 计数)。例如, \([0,1,2,4,5,6,7]\) 在下标 3 处经旋转后可能变为 \([4,5,6,7,0,1,2]\) 。
注意:
1.旋转数组,无论选择多少次都是二分有序。
2.元素可以重复出现, 因此旋转点一定是最小值, 但最小值不一定是旋转点,如\([2,0,2,2,2]\)。因此需要判断左右端点,当左右端点与中点相等,左递增1右递减1,然后再根据单调性来判断。最小值可以根据右端点来比较。
二、 题型
| 序号 | 题目 | 难度 |
|---|---|---|
| —— | ———————————————————————————————————————————————— | ————— |
| 189.轮转数组 | ||
| 33. 搜索旋转排序数组 | 中等 | |
| 81. 搜索旋转排序数组 II | 中等 | |
| 153. 寻找旋转排序数组中的最小值 | 中等 | |
| 154. 寻找旋转排序数组中的最小值 II | 困难 | |
| 剑指 Offer 11. 旋转数组的最小数字 | 中等 | |
| 面试题 10.03. 搜索旋转数组 | 中等 | |
环形数组
一、 定义
数组是 环形 的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。
二、 题型
| 序号 | 题目 | 难度 |
|---|---|---|
| —— | ———————————————————————————————————————————————— | ————— |
| 457. 环形数组是否存在循环 | ||
| 剑指 Offer II 090. 环形房屋偷盗 |
子数组
一、 定义
子数组 是数组的连续子序列。
思路:滑动窗口(可变滑窗)和动态规划。
二、 题型

浙公网安备 33010602011771号