插入排序的三个进化阶段: 

直接插入排序 → 折半插入排序 → 希尔排序
     ↑              ↑              ↑
  基础版        优化比较次数      突破 O(n²)

而「2路插入排序」是折半插入的一种变体,实际很少用,我最后提一下。


一、直接插入排序(基础)[要求会写代码]

思路:就像打扑克,每来一张新牌,插到前面已经排好序的牌里。

代码

public void insertSort(int[] arr) {
    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;
    }
}

复杂度

  • 最好 O(n)(已经有序)

  • 最坏 O(n²)(倒序)

  • 平均 O(n²)

特点:稳定、小数据量最快、对基本有序的数据友好。


二、折半插入排序

优化点:直接插入排序找插入位置时是「挨个比较」,改成「二分查找」。

代码

public void binaryInsertSort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        int key = arr[i];
        int left = 0, right = i - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] > key) right = mid - 1;
            else left = mid + 1;
        }
        // 此时 left 就是要插入的位置
        for (int j = i - 1; j >= left; j--) {
            arr[j + 1] = arr[j];
        }
        arr[left] = key;
    }
}

复杂度

  • 比较次数 O(n log n)(比直接插入好)

  • 移动次数还是 O(n²)(瓶颈没变)

特点:减少了比较次数,但移动次数没变,整体还是 O(n²)。


三、希尔排序(真正突破 O(n²))[要求会写代码]

思路:先分组粗排,再整体细排。让数组基本有序后,最后一轮直接插入排序就很快。

代码


public void shellSort(int[] arr) {
    int n = arr.length;
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int key = arr[i];
            int j = i;
            while (j >= gap && arr[j - gap] > key) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = key;
        }
    }
}

复杂度

  • 最好 O(n log n)

  • 最坏 O(n²)(但很难达到)

  • 平均 ≈ O(n^1.3) ~ O(n^1.5)

特点:不稳定、突破 O(n²)、是插入排序中的天花板。


四、效率对比(重点)

排序算法 最好 最坏 平均 稳定性 推荐场景
直接插入 O(n) O(n²) O(n²) ✅ 稳定 小数据、基本有序
折半插入 O(n log n) O(n²) O(n²) ✅ 稳定 和直接插入差不多
希尔排序 O(n log n) O(n²) O(n^1.3) ❌ 不稳定 大数据、通用
2路插入 O(n²) O(n²) ✅ 稳定 基本不用

真实结论

  • 数据量小(<1000):直接插入就够了(简单、稳定)

  • 数据量大:希尔排序,它是插入排序里最快的

  • 折半插入和2路插入:实际意义不大,了解即可

五、刷题

147. 对链表进行插入排序

LCR 164. 破解闯关密码

更多推荐