2.3 插入排序:日常生活中的排序方式

历史背景

插入排序的灵感来源于人类打扑克牌时的理牌过程——将新摸到的牌插入到手中已排好序的牌中的正确位置。这种算法在 1946 年由 John Mauchly 在关于 EDVAC 计算机的报告中首次正式描述。插入排序虽然简单,但它在数据基本有序时表现极佳,也是希尔排序的基础。

算法原理

插入排序的基本思想是:将数组分为已排序区间和未排序区间。初始时,已排序区间只包含第一个元素。然后依次将未排序区间的元素取出,在已排序区间中找到合适的插入位置,将其插入。重复这个过程,直到未排序区间为空。

Java 实现

java
复制代码
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)。此外,插入排序是在线算法,可以实时处理新到达的数据。

应用场景

  • 小规模数据排序(Java 的 Arrays.sort() 对小于 47 个元素的数组使用插入排序)
  • 数据基本有序的场景
  • 在线排序(数据实时到达)
  • 作为其他高级排序算法(如快速排序、归并排序)的递归基准情形
  • 链表排序(插入排序在链表上表现优秀)