插入排序的灵感来源于人类打扑克牌时的理牌过程——将新摸到的牌插入到手中已排好序的牌中的正确位置。这种算法在 1946 年由 John Mauchly 在关于 EDVAC 计算机的报告中首次正式描述。插入排序虽然简单,但它在数据基本有序时表现极佳,也是希尔排序的基础。
插入排序的基本思想是:将数组分为已排序区间和未排序区间。初始时,已排序区间只包含第一个元素。然后依次将未排序区间的元素取出,在已排序区间中找到合适的插入位置,将其插入。重复这个过程,直到未排序区间为空。
public class InsertionSort {
/**
* 插入排序(基础版本,使用交换实现插入)
*/
public static void sort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int j = i;
while (j > 0 && arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
j--;
}
}
}
/**
* 插入排序(优化版本,使用移动代替交换)
*/
public static void sortOptimized(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
// 将大于 key 的元素向后移动
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
// 插入到正确位置
arr[j + 1] = key;
}
}
/**
* 插入排序(泛型版本)
*/
public static <T extends Comparable<T>> void sortGeneric(T[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
T key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j].compareTo(key) > 0) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
/**
* 插入排序(带二分查找优化)
* 使用二分查找减少比较次数,但移动次数不变
*/
public static void sortBinary(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
// 使用二分查找找到插入位置
int left = 0, right = i;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= key) {
left = mid + 1;
} else {
right = mid;
}
}
// 移动元素
for (int j = i; j > left; j--) {
arr[j] = arr[j - 1];
}
arr[left] = key;
}
}
}
| 指标 | 最好情况 | 最坏情况 | 平均情况 |
|---|---|---|---|
| 时间复杂度 | O(n) | O(n²) | O(n²) |
| 空间复杂度 | O(1) | O(1) | O(1) |
| 比较次数 | n-1 | n(n-1)/2 | n²/4 |
| 移动次数 | 0 | n(n-1)/2 | n²/4 |
插入排序是稳定的排序算法,也是原地排序。它的最大优势在于适应性——对于基本有序的数据,其效率接近 O(n)。此外,插入排序是在线算法,可以实时处理新到达的数据。
Arrays.sort() 对小于 47 个元素的数组使用插入排序)