8.2.插入排序

1. 直接插入排序

核心思想

  1. 每次取出待排序数组中的首元素,将其按关键字大小插入到前面的已排序序列中,直到所有元素都插入完毕。

实现代码(C语言)

// 插入排序函数
void insertionSort(int arr[], int n) {
    int i, key, j;
    for (i = 1; i < n; i++) {
        key = arr[i];
        j = i - 1;

        // 将大于key的元素向后移动
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}

算法特性

  1. 数组已经有序时,最好时间复杂度为\(O(n)\),当数组完全逆序时,最坏时间复杂度为\(O(n^{2})\),平均时间复杂度为\(O(n^{2})\)

  2. 空间复杂度为\(O(1)\),原地排序,插入排序是稳定的排序算法,插入排序对于部分有序的数组非常高效。

  3. 直接插入排序可以用与顺序存储和链式存储的线性标,折半插入排序只能用于顺序存储的线性表。

常见问题

  1. 如何优化插入排序?

    1. 可以使用二分查找来确定插入元素的位置,减少了查找次数,但是交换次数仍然不变,所以优化效果不是非常明显。
  2. 插入排序适用于什么场景?

    1. 插入排序适用于,数据量比较小,或者数组元素已经部分有序的数组,因为插入排序的常数因子较小,实现简单,在小规模数据上表现良好。



2. 希尔排序

核心思想

  1. 希尔排序是插入排序的改进版本。

  2. 希尔排序的核心思想在于将数组分成多个子序列来进行插入排序,子序列不是由连续的元素构成的,而是相隔特定增量的元素构成的。

  3. 对于每个子序列进行插入排序。

  4. 排序完成之后,缩小增量重复2和3,直到增量为1,此时进行插入排序,数组基本有序,插入排序的效率显著提升。

代码实现(C语言)

// 希尔排序函数
void shellSort(int arr[], int n) {
    // 初始增量设为数组长度的一半
    for (int gap = n / 2; gap > 0; gap /= 2) {
        // 对每个子序列进行插入排序
        for (int i = gap; i < n; i++) {
            int temp = arr[i];
            int j;
            // 对当前子序列进行插入操作
            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = temp;
        }
    }
}

算法特性

  1. 希尔排序的时间复杂度依赖于增量序列的选择,所以时间复杂度分析比较困难,当n在某个范围时,希尔排序的时间复杂度约为\(O(n^{1.3})\),最坏情况下的时间复杂度为\(O(n^{2})\)

  2. 空间复杂度为\(O(1)\),原地排序,不稳定排序算法,对于部分有序的数组有较好的性能,能够充分利用插入排序在处理基本有序的数组时效率较高。

  3. 希尔排序只能适用于顺序存储的线性表。

常见问题

  1. 希尔排序为什么比插入排序的效率高?

    1. 希尔排序通过设置不同的增量,使得数组元素快速移动到离其最终位置较近的地方,使得数组接近有序。当增量为1的时候,执行的就是普通插入排序,此时数组基本有序,所以插入排序的效率大大提升,所以整体效率比直接使用插入排序要高。
  2. 代码中增量变化的基本规则是怎样的,有没有最优的增量序列?

    1. 常见增量变化的规则是,初始增量为数组长度的一半,每次将增量得到新增量。

    2. 目前还没有找到理论上最优的增量序列。

posted @ 2025-03-29 15:36  薛定谔的AC  阅读(60)  评论(0)    收藏  举报