引言
大家好!欢迎继续关注我的排序算法系列。今天,我们要学习的是另一种非常基础且重要的排序算法——插入排序 (Insertion Sort)。
插入排序的思路非常贴近我们日常整理扑克牌的方式,理解起来相对自然。虽然它在最坏情况下的效率不高,但在某些特定场景下,它的表现甚至优于一些更高级的排序算法。
什么是插入排序?
想象一下你在玩扑克牌,手里已经握着几张排好序的牌(比如按点数从小到大)。现在你从牌堆里摸了一张新牌,你会怎么做?
通常,你会从右手边(或左手边)已排序的牌开始,逐张比较新牌和手里的牌,找到新牌应该插入的位置,然后将该位置及其后面的牌向后挪动一点,腾出空位,把新牌插进去。
插入排序就是基于这个思想:
- 将整个数组分为两部分: 左边是“已排序”区间,右边是“未排序”区间。
- 初始状态: 已排序区间只包含数组的第一个元素
arr[0]
。 - 逐步扩展: 从未排序区间(从
arr[1]
开始)依次取出元素。 - 寻找位置并插入: 将取出的元素(我们称之为
current
或now
)与已排序区间的元素从右向左逐一比较。 - 移动元素: 如果已排序区间的元素大于
current
,则将该元素向右移动一位。 - 重复比较和移动: 继续向左比较和移动,直到找到一个小于或等于
current
的元素,或者到达已排序区间的开头。 - 插入: 将
current
插入到最后移动元素的那个位置的后面(也就是空出来的位置)。 - 重复: 对未排序区间的所有元素重复步骤 3-7,直到所有元素都被插入到已排序区间中。
算法步骤详解 (以升序为例)
假设我们有数组 [5, 2, 4, 6, 1, 3]
- 初始:
[5] | [2, 4, 6, 1, 3]
(|
分隔已排序和未排序) - 处理
2
(now = 2):- 比较
2
和5
->2 < 5
-> 移动5
->[_, 5] | [4, 6, 1, 3]
j
变为-1
,循环结束。- 插入
2
到j+1
(即 0) ->[2, 5] | [4, 6, 1, 3]
- 比较
- 处理
4
(now = 4):- 比较
4
和5
->4 < 5
-> 移动5
->[2, _, 5] | [6, 1, 3]
- 比较
4
和2
->4 >= 2
->break
循环 (j
为 0)。 - 插入
4
到j+1
(即 1) ->[2, 4, 5] | [6, 1, 3]
- 比较
- 处理
6
(now = 6):- 比较
6
和5
->6 >= 5
->break
循环 (j
为 2)。 - 插入
6
到j+1
(即 3) ->[2, 4, 5, 6] | [1, 3]
- 比较
- 处理
1
(now = 1):- 比较
1
和6
->1 < 6
-> 移动6
->[2, 4, 5, _, 6] | [3]
- 比较
1
和5
->1 < 5
-> 移动5
->[2, 4, _, 5, 6] | [3]
- 比较
1
和4
->1 < 4
-> 移动4
->[2, _, 4, 5, 6] | [3]
- 比较
1
和2
->1 < 2
-> 移动2
->[_, 2, 4, 5, 6] | [3]
j
变为-1
,循环结束。- 插入
1
到j+1
(即 0) ->[1, 2, 4, 5, 6] | [3]
- 比较
- 处理
3
(now = 3):- 比较
3
和6
->3 < 6
-> 移动6
->[1, 2, 4, 5, _, 6]
- 比较
3
和5
->3 < 5
-> 移动5
->[1, 2, 4, _, 5, 6]
- 比较
3
和4
- 比较