插入排序¶
概述¶
插入排序(Insertion Sort)是一种简单直观的排序算法,其基本思想是:将数组分为已排序和未排序两部分,每次将未排序部分的第一个元素插入到已排序部分的适当位置。
核心特性
- 稳定排序:相等元素的相对顺序保持不变
- 原地排序:空间复杂度 O(1)
- 在线算法:可以边接收数据边排序
- 自适应排序:对基本有序的数据效率高
生活类比
想象你在玩扑克牌整理手牌:你拿着已排好序的牌,每次从牌堆抽一张新牌,然后从右往左找到合适的位置插入。插入排序的工作方式完全一样——每次取一个元素,插入到正确位置。
算法原理¶
基本思想¶
flowchart TB
subgraph "插入排序核心思想"
A["将第一个元素视为已排序序列"] --> B["取下一个元素 key"]
B --> C["从后向前扫描已排序序列"]
C --> D{"找到插入位置?"}
D -->|否| E["元素后移,继续扫描"]
D -->|是| F["插入 key 到正确位置"]
E --> C
F --> G{"还有未排序元素?"}
G -->|是| B
G -->|否| H["排序完成"]
end
style H fill:#E8F5E9,stroke:#4CAF50
排序过程可视化¶
插入操作详解¶
sequenceDiagram
participant Array as 数组
participant Key as key=4
participant J as 指针j
Array->>Key: 取出 arr[i]=4
Key->>J: j = i-1 = 2
loop j >= 0 且 arr[j] > key
J->>Array: arr[j+1] = arr[j] (后移)
Array-->>Array: [3,5,8,8,2]
J->>J: j-- = 1
end
J->>Array: arr[j+1] = key
Array-->>Array: [3,4,5,8,2]
基本实现¶
优化版本¶
二分插入排序¶
使用二分查找确定插入位置,减少比较次数。
graph LR
subgraph "比较次数对比"
A["基本插入排序<br>O(i) 次比较"] --> B["二分插入排序<br>O(log i) 次比较"]
end
style B fill:#E8F5E9,stroke:#4CAF50
希尔排序(分组插入排序)¶
希尔排序是插入排序的高效改进版本,通过分组实现远距离元素的快速移动。
flowchart TB
subgraph "希尔排序思想"
A["选择间隔 gap"] --> B["对每个 gap 分组进行插入排序"]
B --> C["减小 gap"]
C --> D{"gap > 0?"}
D -->|是| B
D -->|否| E["最后进行标准插入排序<br>(gap=1)"]
end
style E fill:#E8F5E9,stroke:#4CAF50
成对插入排序¶
减少比较和交换次数。
复杂度分析¶
时间复杂度¶
graph TB
subgraph "时间复杂度分析"
A["最好情况 O(n)"] --> A1["数组已有序<br>每个元素比较1次"]
B["平均情况 O(n²)"] --> B1["随机数据<br>约 n²/4 次比较和移动"]
C["最坏情况 O(n²)"] --> C1["数组逆序<br>n(n-1)/2 次比较和移动"]
end
style A fill:#E8F5E9,stroke:#4CAF50
style C fill:#FFF3E0,stroke:#FF9800
| 情况 | 时间复杂度 | 比较次数 | 移动次数 | 说明 |
|---|---|---|---|---|
| 最好 | O(n) | n-1 | 0 | 数组已有序 |
| 平均 | O(n²) | n²/4 | n²/4 | 随机数据 |
| 最坏 | O(n²) | n(n-1)/2 | n(n-1)/2 | 数组逆序 |
逆序对分析¶
插入排序与逆序对
插入排序的移动次数等于数组中的逆序对数量。
逆序对:如果 i < j 但 arr[i] > arr[j],则 (arr[i], arr[j]) 是一个逆序对。
| Text Only | |
|---|---|
空间复杂度¶
O(1),原地排序。
稳定性分析¶
与其他排序算法对比¶
graph TB
subgraph "O(n²) 排序算法对比"
A["插入排序<br>最好 O(n)"]
B["冒泡排序<br>最好 O(n)"]
C["选择排序<br>最好 O(n²)"]
end
A --> A1["✓ 稳定<br>✓ 对有序数据高效<br>✓ 在线算法"]
B --> B1["✓ 稳定<br>✓ 对有序数据高效<br>✗ 交换开销大"]
C --> C1["✗ 不稳定<br>✗ 任何情况都 O(n²)<br>✓ 交换次数少"]
style A fill:#E3F2FD,stroke:#2196F3
| 特性 | 插入排序 | 冒泡排序 | 选择排序 |
|---|---|---|---|
| 最好情况 | O(n) | O(n) | O(n²) |
| 平均情况 | O(n²) | O(n²) | O(n²) |
| 稳定性 | 稳定 | 稳定 | 不稳定 |
| 交换次数 | 等于逆序对数 | 最多 n(n-1)/2 | 最多 n-1 |
| 适用场景 | 小数据、基本有序 | 小数据 | 交换代价大时 |
链表插入排序¶
链表的插入排序更加高效,因为插入操作不需要移动元素。
flowchart LR
subgraph "链表插入排序"
A["原链表: 4→2→1→3"] --> B["取出节点"]
B --> C["在已排序部分找位置"]
C --> D["插入节点"]
D --> E["结果: 1→2→3→4"]
end
style E fill:#E8F5E9,stroke:#4CAF50
应用场景¶
1. 小规模数据排序¶
2. 基本有序数据的排序¶
| Text Only | |
|---|---|
3. 在线排序¶
4. 折半插入优化大规模数据¶
常见问题与陷阱¶
1. 边界条件¶
2. 稳定性破坏¶
| C | |
|---|---|
总结¶
算法特点¶
mindmap
root((插入排序))
基本特性
时间 O(n²)
空间 O(1)
稳定排序
原地排序
优点
实现简单
小数据高效
有序数据 O(n)
在线算法
适合链表
缺点
大数据效率低
移动开销大
优化
二分插入
希尔排序
成对插入
应用
小规模排序
有序数据
在线处理
链表排序
学习建议¶
- 理解原理:掌握"插入"的核心思想
- 动手实现:从基本版本开始,逐步添加优化
- 分析复杂度:理解最好、最坏、平均情况
- 对比学习:与冒泡排序、选择排序对比
- 了解优化:学习希尔排序的分组思想
参考资料¶
- 《算法导论》第2章 - 插入排序
- 《数据结构与算法分析》第7章 - 排序
- Insertion Sort - Wikipedia
- Shellsort - Wikipedia