✨ feat: 添加文档功能和页面模板
This commit is contained in:
451
web/doc/2.2 数组的插入与删除.md
Normal file
451
web/doc/2.2 数组的插入与删除.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# 2.2 数组的插入与删除
|
||||
|
||||
---
|
||||
|
||||
## 一、先把数组想成一排固定座位
|
||||
|
||||
今天我们只学两件事:
|
||||
|
||||
- 往数组里放进一个新元素,这叫**插入**。
|
||||
- 从数组里拿走一个元素,这叫**删除**。
|
||||
|
||||
数组可以想成教室里一排**紧紧挨着的固定座位**,每个位置都不能乱跳。
|
||||
|
||||
比如下面这个数组:
|
||||
|
||||
| 位置(第几个) | 第1个 | 第2个 | 第3个 | 第4个 | 第5个 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 下标 | 0 | 1 | 2 | 3 | 4 |
|
||||
| 内容 | 12 | 18 | 23 | 31 | 40 |
|
||||
|
||||
这里要特别注意:
|
||||
|
||||
- 平时说“第 1 个位置、第 2 个位置”,是按照人的习惯数的。
|
||||
- 在 C++ 数组里,下标从 `0` 开始。
|
||||
|
||||
所以:
|
||||
|
||||
- 第 1 个位置,对应下标 `0`
|
||||
- 第 3 个位置,对应下标 `2`
|
||||
- 第 `pos` 个位置,对应下标 `pos - 1`
|
||||
|
||||
> 这节课讲图的时候,我们主要说“第几个位置”;写代码的时候,再换成下标。
|
||||
|
||||
---
|
||||
|
||||
## 二、为什么数组的插入和删除要“搬家”?
|
||||
|
||||
数组里的元素是**连续排在一起**的。
|
||||
|
||||
这就像一排已经坐好的同学:
|
||||
|
||||
- 想在中间插入一个人,就得有人挪一挪,腾出空位。
|
||||
- 想删掉中间一个人,就会空出一个洞,后面的人要补上来。
|
||||
|
||||
所以数组的插入和删除,最关键的不是“放进去”或“拿出来”,而是:
|
||||
|
||||
> **怎样移动其他元素,才能既不丢数据,也不弄乱顺序。**
|
||||
|
||||
这就是本节最重要的地方。
|
||||
|
||||
---
|
||||
|
||||
## 三、数组插入:先腾位置,再放新元素
|
||||
|
||||
### 1. 例子:在第 3 个位置插入 20
|
||||
|
||||
原数组:
|
||||
|
||||
```text
|
||||
位置: 1 2 3 4 5 6
|
||||
内容: [12] [18] [23] [31] [40] [ ]
|
||||
```
|
||||
|
||||
注意:这里第 6 个位置先留空,表示数组容量足够大,可以插入新元素。
|
||||
|
||||
现在要把 `20` 插入到**第 3 个位置**。
|
||||
|
||||
插入后应该变成:
|
||||
|
||||
```text
|
||||
位置: 1 2 3 4 5 6
|
||||
内容: [12] [18] [20] [23] [31] [40]
|
||||
```
|
||||
|
||||
### 2. 插入时,元素是怎样移动的?
|
||||
|
||||
要想让第 3 个位置空出来,原来第 3、4、5 个位置上的元素都要向后移动一格。
|
||||
|
||||
但一定要记住:
|
||||
|
||||
> **插入时,要从后往前移。**
|
||||
|
||||
移动过程如下:
|
||||
|
||||
```text
|
||||
原来: [12] [18] [23] [31] [40] [ ]
|
||||
第1步: [12] [18] [23] [31] [40] [40] 把 40 往后移
|
||||
第2步: [12] [18] [23] [31] [31] [40] 把 31 往后移
|
||||
第3步: [12] [18] [23] [23] [31] [40] 把 23 往后移
|
||||
第4步: [12] [18] [20] [23] [31] [40] 把 20 放进第 3 个位置
|
||||
```
|
||||
|
||||
### 3. 为什么插入要从后往前移?
|
||||
|
||||
因为如果你从前往后移,前面的数据会把后面的数据盖掉。
|
||||
|
||||
来看一个错误示范。还是在第 3 个位置插入 `20`:
|
||||
|
||||
```text
|
||||
原来: [12] [18] [23] [31] [40] [ ]
|
||||
|
||||
错误地从前往后移:
|
||||
第1步: [12] [18] [23] [23] [40] [ ]
|
||||
第2步: [12] [18] [23] [23] [23] [ ]
|
||||
第3步: [12] [18] [23] [23] [23] [23]
|
||||
```
|
||||
|
||||
你会发现:原来的 `31` 和 `40` 都不见了!
|
||||
|
||||
原因是:
|
||||
|
||||
- 你先把 `23` 复制到了后面。
|
||||
- 后面的位置被改掉以后,再继续搬,就只能搬到已经改坏的数据。
|
||||
|
||||
所以插入时一定要:
|
||||
|
||||
> **从最后一个元素开始,倒着往前挪。**
|
||||
|
||||
### 4. 插入的步骤口诀
|
||||
|
||||
可以记成一句话:
|
||||
|
||||
> **先留空,从后搬,最后放。**
|
||||
|
||||
具体就是:
|
||||
|
||||
1. 先保证数组后面有空位置。
|
||||
2. 从最后一个元素开始,依次向后移动一格。
|
||||
3. 移到目标位置后停下。
|
||||
4. 把新元素放进去。
|
||||
5. 数组长度 `n` 加 1。
|
||||
|
||||
### 5. 插入的课堂版 C++ 代码
|
||||
|
||||
下面的代码约定:
|
||||
|
||||
- 数组现在有 `n` 个元素。
|
||||
- 要把 `x` 插入到第 `pos` 个位置。
|
||||
- `pos` 从 `1` 开始数。
|
||||
- 数组容量足够大。
|
||||
|
||||
```cpp
|
||||
for (int i = n; i >= pos; i--) {
|
||||
a[i] = a[i - 1];
|
||||
}
|
||||
a[pos - 1] = x;
|
||||
n++;
|
||||
```
|
||||
|
||||
你可以这样理解这段循环:
|
||||
|
||||
- `i = n` 时,把最后一个元素搬到新位置。
|
||||
- `i = n - 1` 时,把倒数第二个元素往后搬。
|
||||
- 一直搬到第 `pos` 个位置为止。
|
||||
|
||||
---
|
||||
|
||||
## 四、数组删除:先补空位,再缩短长度
|
||||
|
||||
### 1. 例子:删除第 3 个位置的元素
|
||||
|
||||
原数组:
|
||||
|
||||
```text
|
||||
位置: 1 2 3 4 5
|
||||
内容: [ 8] [11] [14] [17] [20]
|
||||
```
|
||||
|
||||
现在要删除第 3 个位置上的 `14`。
|
||||
|
||||
删除后应该变成:
|
||||
|
||||
```text
|
||||
位置: 1 2 3 4
|
||||
内容: [ 8] [11] [17] [20]
|
||||
```
|
||||
|
||||
### 2. 删除时,元素是怎样移动的?
|
||||
|
||||
第 3 个位置删掉以后,会空出一个位置。后面的元素要依次往前补。
|
||||
|
||||
注意:
|
||||
|
||||
> **删除时,要从前往后移。**
|
||||
|
||||
移动过程如下:
|
||||
|
||||
```text
|
||||
原来: [ 8] [11] [14] [17] [20]
|
||||
第1步: [ 8] [11] [17] [17] [20] 把 17 往前移
|
||||
第2步: [ 8] [11] [17] [20] [20] 把 20 往前移
|
||||
最后: [ 8] [11] [17] [20] 长度减 1
|
||||
```
|
||||
|
||||
最后那个多出来的 `20` 虽然还留在原来的地方,但因为数组长度已经减 1,所以它已经**不算数组的一部分了**。
|
||||
|
||||
### 3. 为什么删除要从前往后移?
|
||||
|
||||
因为空位在前面,应该让后面的元素一个一个补上来。
|
||||
|
||||
如果你反过来,从后往前移,也会出问题。还是删除第 3 个位置:
|
||||
|
||||
```text
|
||||
原来: [ 8] [11] [14] [17] [20]
|
||||
|
||||
错误地从后往前移:
|
||||
第1步: [ 8] [11] [14] [20] [20]
|
||||
第2步: [ 8] [11] [20] [20] [20]
|
||||
```
|
||||
|
||||
原来的 `17` 被弄丢了。
|
||||
|
||||
所以删除时要记住:
|
||||
|
||||
> **从缺口的后面开始,按顺序往前补。**
|
||||
|
||||
### 4. 删除的步骤口诀
|
||||
|
||||
可以记成一句话:
|
||||
|
||||
> **先删掉,从前搬,最后减。**
|
||||
|
||||
具体就是:
|
||||
|
||||
1. 找到要删除的位置。
|
||||
2. 让后面的元素依次向前移动一格。
|
||||
3. 数组长度 `n` 减 1。
|
||||
|
||||
### 5. 删除的课堂版 C++ 代码
|
||||
|
||||
下面的代码约定:
|
||||
|
||||
- 数组现在有 `n` 个元素。
|
||||
- 删除第 `pos` 个位置上的元素。
|
||||
- `pos` 从 `1` 开始数。
|
||||
|
||||
```cpp
|
||||
for (int i = pos; i < n; i++) {
|
||||
a[i - 1] = a[i];
|
||||
}
|
||||
n--;
|
||||
```
|
||||
|
||||
你可以这样理解这段循环:
|
||||
|
||||
- `i = pos` 时,把后一个元素补到空位上。
|
||||
- `i = pos + 1` 时,再把更后面的元素补上来。
|
||||
- 一直补到最后一个元素为止。
|
||||
|
||||
---
|
||||
|
||||
## 五、插入和删除放在一起比较
|
||||
|
||||
| 操作 | 会发生什么 | 正确移动方向 | 为什么 |
|
||||
|:---:|:---|:---:|:---|
|
||||
| 插入 | 中间要腾出一个空位 | 从后往前 | 先保护后面的数据,不让它们被覆盖 |
|
||||
| 删除 | 中间会出现一个缺口 | 从前往后 | 让后面的数据按顺序补上来 |
|
||||
|
||||
再记一遍:
|
||||
|
||||
- **插入:从后往前移**
|
||||
- **删除:从前往后移**
|
||||
|
||||
这是本节最重要的结论。
|
||||
|
||||
---
|
||||
|
||||
## 六、两个最容易忽略的小细节
|
||||
|
||||
### 1. 插入前,数组要有空位
|
||||
|
||||
如果数组已经装满了,就不能直接插入。
|
||||
|
||||
比如数组最多只能放 5 个数,现在已经有 5 个数了,再插入第 6 个,就没有地方放了。
|
||||
|
||||
所以写程序时,常常会这样做:
|
||||
|
||||
- 开一个更大的数组,比如最多放 `100` 个数。
|
||||
- 实际已经用了多少个,用变量 `n` 记录。
|
||||
|
||||
### 2. 删除后,不是把数“擦掉”,而是把长度减 1
|
||||
|
||||
数组删除一个元素后,最后那个位置里可能还保留着旧数值,但这已经不重要了。
|
||||
|
||||
因为程序只看前 `n` 个元素:
|
||||
|
||||
- 删除前看前 `n` 个
|
||||
- 删除后只看前 `n - 1` 个
|
||||
|
||||
所以真正关键的是:
|
||||
|
||||
> **长度变了,数组有效范围也变了。**
|
||||
|
||||
---
|
||||
|
||||
## 七、特殊情况也要会判断
|
||||
|
||||
### 1. 在最后面插入
|
||||
|
||||
如果要在第 `n + 1` 个位置插入,也就是直接接到数组末尾:
|
||||
|
||||
```text
|
||||
[12] [18] [23] [31] [40] -> [12] [18] [23] [31] [40] [50]
|
||||
```
|
||||
|
||||
这时不需要搬动任何元素,直接放进去即可。
|
||||
|
||||
### 2. 删除最后一个元素
|
||||
|
||||
如果删除的是最后一个元素:
|
||||
|
||||
```text
|
||||
[12] [18] [23] [31] [40] -> [12] [18] [23] [31]
|
||||
```
|
||||
|
||||
这时也不需要搬动任何元素,只要把长度减 1 就行。
|
||||
|
||||
---
|
||||
|
||||
## 八、最容易犯的 5 个错误
|
||||
|
||||
### 错误 1:插入时移动方向写反
|
||||
|
||||
结果:后面的数据会被覆盖。
|
||||
|
||||
### 错误 2:删除时移动方向写反
|
||||
|
||||
结果:中间的数据会丢失。
|
||||
|
||||
### 错误 3:忘记更新 `n`
|
||||
|
||||
插入后忘记 `n++`,新元素虽然放进去了,但程序可能不把它算进去。
|
||||
|
||||
删除后忘记 `n--`,旧数据虽然应该消失了,但程序还会把它输出出来。
|
||||
|
||||
### 错误 4:把“第几个位置”和“下标”搞混
|
||||
|
||||
记住:
|
||||
|
||||
- 第 `1` 个位置,对应下标 `0`
|
||||
- 第 `pos` 个位置,对应下标 `pos - 1`
|
||||
|
||||
### 错误 5:插入时没有预留空间
|
||||
|
||||
结果:数组越界,程序可能出错。
|
||||
|
||||
---
|
||||
|
||||
## 九、一张总图记住本节内容
|
||||
|
||||
```text
|
||||
数组插入:先腾空位
|
||||
|
||||
[12] [18] [23] [31] [40] [ ]
|
||||
← ← ←
|
||||
[12] [18] [20] [23] [31] [40]
|
||||
|
||||
|
||||
数组删除:先补缺口
|
||||
|
||||
[ 8] [11] [14] [17] [20]
|
||||
→ →
|
||||
[ 8] [11] [17] [20]
|
||||
```
|
||||
|
||||
记忆口诀:
|
||||
|
||||
> **插入看后面,从后往前搬;删除看前面,从前往后搬。**
|
||||
|
||||
---
|
||||
|
||||
## 十、课堂版完整示例
|
||||
|
||||
下面是一份把“插入”和“删除”都写进去的课堂版程序。为了简单起见,输入位置 `pos` 按“第几个位置”来理解,也就是从 `1` 开始数。
|
||||
|
||||
```cpp
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
const int MAXN = 105;
|
||||
int a[MAXN];
|
||||
int n;
|
||||
|
||||
void insertValue(int pos, int x) {
|
||||
for (int i = n; i >= pos; i--) {
|
||||
a[i] = a[i - 1];
|
||||
}
|
||||
a[pos - 1] = x;
|
||||
n++;
|
||||
}
|
||||
|
||||
void deleteValue(int pos) {
|
||||
for (int i = pos; i < n; i++) {
|
||||
a[i - 1] = a[i];
|
||||
}
|
||||
n--;
|
||||
}
|
||||
|
||||
int main() {
|
||||
cin >> n;
|
||||
for (int i = 0; i < n; i++) {
|
||||
cin >> a[i];
|
||||
}
|
||||
|
||||
int pos, x;
|
||||
cin >> pos >> x;
|
||||
insertValue(pos, x);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
cout << a[i] << " ";
|
||||
}
|
||||
cout << "\n";
|
||||
|
||||
cin >> pos;
|
||||
deleteValue(pos);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
cout << a[i] << " ";
|
||||
}
|
||||
cout << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、自己试一试
|
||||
|
||||
### 1. 观察移动过程
|
||||
|
||||
把 `9` 插入到下面数组的第 2 个位置,请你把每一步移动都画出来:
|
||||
|
||||
```text
|
||||
[3] [7] [12] [15] [ ]
|
||||
```
|
||||
|
||||
### 2. 观察删除过程
|
||||
|
||||
删除下面数组的第 4 个位置,请你把每一步移动都画出来:
|
||||
|
||||
```text
|
||||
[5] [8] [11] [14] [20]
|
||||
```
|
||||
|
||||
### 3. 想一想
|
||||
|
||||
- 为什么插入和删除都可能需要移动很多个元素?
|
||||
- 如果总是在数组最前面插入,会不会很麻烦?
|
||||
|
||||
如果你能清楚地回答这两个问题,就说明你已经真正理解数组的插入和删除了。
|
||||
Reference in New Issue
Block a user