一、问题背景:我为什么会研究插入排序?
昨天写完冒泡排序,我突然想到:排序的"亲兄弟"其实有一个叫插入排序。
我最早学它的时候,觉得它比冒泡更自然,因为它跟我们平时 整理扑克牌 的方式几乎一模一样。
比如打牌时,我们一张一张把牌插到已经排好序的牌堆里,这不就是插入排序的核心思想吗?
当时刚学的时候,我老觉得冒泡像是"傻力气" ------ 不断交换,而插入排序更像是"聪明点的做法",有点策略。
二、插入排序到底是什么?
一句话总结:
插入排序就是把数组分成"已排序区"和"未排序区",每次从未排序区取一个数,插到已排序区的合适位置。
比方说你在打牌:
- 起手拿一张牌 → 这时候它自己就是"有序的"。
- 再摸一张牌 → 你会把它插到前一张的前面或后面,保持顺序。
- 再摸第三张 → 你会找个合适的位置把它插进去。
- 最后所有的牌都是有序的。
三、算法过程(举个例子)
假设要排序 [5, 3, 8, 4, 2]
:
[5]
→ 第一个元素自然有序。- 插入 3 →
[3, 5]
- 插入 8 →
[3, 5, 8]
- 插入 4 →
[3, 4, 5, 8]
- 插入 2 →
[2, 3, 4, 5, 8]
就这样,数组就排好序了。
四、Java 代码实现
我用 Java 写一个最基础版本:
java
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
// 当前待插入的元素
int key = arr[i];
int j = i - 1;
// 从已排序区从后往前找合适位置
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]; // 向后挪位置
j--;
}
arr[j + 1] = key; // 插入到正确位置
}
}
public static void main(String[] args) {
int[] arr = {5, 3, 8, 4, 2};
insertionSort(arr);
// 打印结果
for (int num : arr) {
System.out.print(num + " ");
}
}
}
运行结果:
2 3 4 5 8
五、时间复杂度 & 空间复杂度
八股来了 🤓:
- 最好情况(数组本来有序):O(n) ------ 因为只需比较,不需要移动。
- 最坏情况(数组完全逆序):O(n²) ------ 每次插入都要移动所有已排序元素。
- 平均情况:O(n²)。
- 空间复杂度:O(1),原地排序。
- 稳定性:插入排序是稳定的,因为相等元素不会被交换顺序。
六、面试八股模拟 Q&A
我帮你模拟一下可能的面试场景:
Q1:插入排序的思想是什么?
A1:维护一个有序区和无序区,每次从无序区取一个数,插入到有序区的合适位置。
Q2:插入排序的时间复杂度?
A2:最好 O(n),最坏 O(n²),平均 O(n²),空间复杂度 O(1)。
Q3:插入排序稳定吗?
A3:是的,插入排序是稳定的。
Q4:和冒泡排序的区别?
A4:冒泡是通过不断交换相邻元素实现排序,而插入排序是通过移动元素找到合适位置再插入。插入排序通常交换次数更少,更接近我们手工整理数据的思维方式。
Q5:插入排序适合什么场景?
A5:适合 数据量小 、基本有序 的场景。比如给小数组排序,或者用在更高级排序算法(如希尔排序)的子过程。
七、个人感悟
我第一次写插入排序的时候,写成了"先交换再比较",结果调了半天 bug,后来才意识到 关键在于"挪动"而不是"交换" 。
这也让我体会到:虽然冒泡和插入的复杂度都一样,但思想差别很大。
另外,很多大厂面试时会问:"如果数组基本有序,你会选什么排序?"
这个时候答"插入排序"是个加分项,因为它在这种情况下能接近 O(n)。