Java 插入排序实现
以下是插入排序在 Java 中的完整实现,包含多种写法和详细注释。
基础版本
java
public class InsertionSort {
/**
* 插入排序基础实现 (升序)
* @param arr 待排序的数组
*/
public static void insertionSort(int[] arr) {
// 从第二个元素开始遍历(第一个元素默认已排序)
for (int i = 1; i < arr.length; i++) {
int key = arr[i]; // 当前待插入的元素
int j = i - 1; // 从当前元素的前一个位置开始比较
// 将比key大的元素都向后移动一位
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
// 将key插入到正确位置
arr[j + 1] = key;
}
}
public static void main(String[] args) {
int[] arr = {5, 2, 4, 6, 1, 3};
System.out.println("排序前: " + Arrays.toString(arr));
insertionSort(arr);
System.out.println("排序后: " + Arrays.toString(arr));
}
}
完整实现(带详细注释和测试)
java
import java.util.Arrays;
public class InsertionSort {
/**
* 插入排序 - 基础版本
* 时间复杂度: 最好O(n), 最坏O(n²), 平均O(n²)
* 空间复杂度: O(1)
* 稳定性: 稳定
*/
public static void insertionSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
// 从第二个元素开始(索引1),因为第一个元素默认已排序
for (int i = 1; i < arr.length; i++) {
int key = arr[i]; // 当前要插入的元素
int j = i - 1; // 从i的前一个位置开始比较
// 在已排序部分从后往前查找插入位置
// 同时将比key大的元素向后移动
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]; // 元素后移
j--;
}
// 将key插入到正确位置
arr[j + 1] = key;
// 打印每轮排序结果(可选,用于理解算法过程)
// System.out.println("第" + i + "轮: " + Arrays.toString(arr));
}
}
/**
* 插入排序 - 泛型版本,支持任何实现了Comparable接口的类型
*/
public static <T extends Comparable<T>> void insertionSortGeneric(T[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 1; i < arr.length; 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 <T> void insertionSortWithComparator(T[] arr, java.util.Comparator<T> comparator) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 1; i < arr.length; i++) {
T key = arr[i];
int j = i - 1;
while (j >= 0 && comparator.compare(arr[j], key) > 0) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
/**
* 插入排序 - 降序排列
*/
public static void insertionSortDesc(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 1; i < arr.length; 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 binaryInsertionSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 1; i < arr.length; i++) {
int key = arr[i];
// 使用二分查找找到插入位置
int left = 0;
int right = i - 1;
int pos = i; // 默认插入位置
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] > key) {
pos = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
// 将元素向后移动
for (int j = i - 1; j >= pos; j--) {
arr[j + 1] = arr[j];
}
// 插入元素
arr[pos] = key;
}
}
// 测试方法
public static void main(String[] args) {
System.out.println("=== 插入排序测试 ===");
// 测试1: 基础版本
int[] arr1 = {5, 2, 4, 6, 1, 3};
System.out.println("原始数组: " + Arrays.toString(arr1));
insertionSort(arr1);
System.out.println("升序排序: " + Arrays.toString(arr1));
// 测试2: 降序版本
int[] arr2 = {5, 2, 4, 6, 1, 3};
insertionSortDesc(arr2);
System.out.println("降序排序: " + Arrays.toString(arr2));
// 测试3: 泛型版本
Integer[] arr3 = {5, 2, 4, 6, 1, 3};
System.out.println("泛型排序前: " + Arrays.toString(arr3));
insertionSortGeneric(arr3);
System.out.println("泛型排序后: " + Arrays.toString(arr3));
// 测试4: 字符串排序
String[] arr4 = {"banana", "apple", "cherry", "date"};
System.out.println("字符串排序前: " + Arrays.toString(arr4));
insertionSortGeneric(arr4);
System.out.println("字符串排序后: " + Arrays.toString(arr4));
// 测试5: 二分插入排序
int[] arr5 = {5, 2, 4, 6, 1, 3};
System.out.println("二分插入前: " + Arrays.toString(arr5));
binaryInsertionSort(arr5);
System.out.println("二分插入后: " + Arrays.toString(arr5));
// 测试6: 性能比较
System.out.println("\n=== 性能测试 ===");
int[] largeArr = new int[10000];
for (int i = 0; i < largeArr.length; i++) {
largeArr[i] = (int) (Math.random() * 10000);
}
int[] arrCopy1 = largeArr.clone();
int[] arrCopy2 = largeArr.clone();
long startTime = System.currentTimeMillis();
insertionSort(arrCopy1);
long endTime = System.currentTimeMillis();
System.out.println("普通插入排序时间: " + (endTime - startTime) + "ms");
startTime = System.currentTimeMillis();
binaryInsertionSort(arrCopy2);
endTime = System.currentTimeMillis();
System.out.println("二分插入排序时间: " + (endTime - startTime) + "ms");
}
}
算法特点总结
时间复杂度:
· 最优情况(已排序):O(n)
· 最差情况(逆序):O(n²)
· 平均情况:O(n²)
空间复杂度: O(1) - 原地排序
稳定性: 稳定 - 相等元素的相对位置不变
适用场景:
· 小规模数据排序
· 基本有序的数据
· 作为更复杂算法的子过程
关键点说明
- 核心思想:将数组分为已排序和未排序两部分,逐个将未排序元素插入到已排序部分的正确位置。
- 内层循环:从后往前比较,同时移动元素,为插入腾出空间。
- 插入位置:j + 1 是最终插入位置,因为循环结束时 j 指向的是第一个不大于 key 的元素。
- 边界情况:处理空数组或单元素数组时直接返回。
这个实现包含了插入排序的各种变体,可以根据具体需求选择合适的版本。