408答疑
文章目录
二、插入排序
基本概念
- 定义:插入排序是一种简单直观的排序算法,其基本思想是每次将一个待排序的记录按其关键字大小插入前面已排好序的子序列,直到全部记录插入完成。
插入排序方法
-
直接插入排序:将待排元素往已排序好的序列进行插入,叫做直接插入排序。
-
折半插入排序:由插入排序的思想可以引申出折半插入排序。
-
希尔排序:由插入排序的思想可以引申出希尔排序。
直接插入排序
算法描述
- 基本思想:直接插入排序是一种最简单也最直观的排序算法。其基本思想是每次将一个待排序的记录按其关键字大小插入前面已排好序的子序列,直到全部记录插入完成。
-
操作步骤:要将元素 L ( i ) L(i) L(i) 插入已有序的子序列 L [ 1... i − 1 ] L[1...i-1] L[1...i−1],需要执行以下操作(下面用 L [ ] L[] L[] 表示一个表,而用 L ( ) L() L() 表示一个元素):
- 查找出 L ( i ) L(i) L(i) 在 L [ 1... i − 1 ] L[1...i-1] L[1...i−1] 中的插入位置 k k k。
- 将 L [ k . . . i − 1 ] L[k...i-1] L[k...i−1] 中的所有元素依次后移一个位置。
- 将 L ( i ) L(i) L(i) 复制到 L ( k ) L(k) L(k)。
-
实现过程:为了实现对 L [ 1... n ] L[1...n] L[1...n] 的排序,可以将 L ( 2 ) L(2) L(2) ~ L ( n ) L(n) L(n) 依次插入前面已排好序的子序列,初始 L [ 1 ] L[1] L[1] 可以视为一个已排好序的子序列。
-
执行次数:共进行 n − 1 n-1 n−1 次插入操作(从 L ( 2 ) L(2) L(2) 到 L ( n ) L(n) L(n))。
-
原地排序:仅需常数级辅助空间,空间复杂度为 O ( 1 ) O(1) O(1)。
示例
-
初始序列: 4 9 1 , 38 , 65 , 97 , 76 , 13 , 27 , 4 9 2 49_1, 38, 65, 97, 76, 13, 27, 49_2 491,38,65,97,76,13,27,492。
-
排序过程:
初始时 49 可以视为一个已排好序的子序列,按照上述算法进行直接插入排序的过程如下图所示,括号内是已排好序的子序列。
性能分析
-
空间效率:仅使用了常数个辅助单元,因而空间复杂度为 O ( 1 ) O(1) O(1)。
-
时间效率:
- 在排序过程中,向有序子表中逐个地插入元素的操作进行了 n − 1 n-1 n−1 趟,每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。
- 在最好情况下,表中元素已经有序,此时每插入一个元素,都只需比较一次而不用移动元素,因而时间复杂度为 O ( n ) O(n) O(n)。
- 在最坏情况下,表中元素顺序刚好与排序结果中的元素顺序相反(逆序),总的比较次数达到最大,总的移动次数也达到最大,总的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
- 平均情况下,考虑待排序表中元素是随机的,此时可以取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,总的比较次数与总的移动次数约为 n 2 / 4 n^2/4 n2/4。
- 因此,直接插入排序算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
-
稳定性:因为每次插入元素时总是从后往前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序算法。
-
适用性:直接插入排序适用于顺序存储和链式存储的线性表,采用链式存储时无须移动元素。
折半插入排序
改进点
- 折半插入排序算法对直接插入排序算法进行了改进,主要在于查找待插入元素位置的方法。通过使用折半查找来确定待插入元素的位置,从而减少比较次数。
算法步骤
- 将待插入的元素暂存到数组的第一个位置。
- 使用折半查找确定该元素在已排序子序列中的正确位置。
- 从后向前移动元素,为新元素腾出空间。
- 将新元素插入到正确的位置。
性能分析
-
时间复杂度:折半插入排序的时间复杂度约为 O ( n log 2 n ) O(n\log_2 n) O(nlog2n),这是因为折半查找减少了比较元素的次数。然而,由于元素的移动次数并未改变,总的时间复杂度仍为 O ( n 2 ) O(n^2) O(n2)。对于数据量不大的排序表,折半插入排序往往能表现出很好的性能。
-
稳定性:折半插入排序是一种稳定的排序算法,因为它在插入元素时总是从后往前先比较再移动,不会出现相同元素相对位置发生变化的情况。
-
适用性:折半插入排序仅适用于顺序存储的线性表,因为它依赖于顺序存储的线性结构来实现折半查找和元素的移动。
希尔排序
相关概念
-
基本思想:希尔排序是基于直接插入排序进行改进而得来,又称缩小增量排序。它更适用于基本有序的排序表和数据量不大的排序表。
-
排序过程:先将待排序表分割成若干形如 L [ i , i + d , i + 2 d , ⋯ , i + k d ] L[i, i+d, i+2d, \cdots, i+kd] L[i,i+d,i+2d,⋯,i+kd] 的“特殊”子表,即相隔某个“增量”的记录组成一个子表,对各个子表分别进行直接插入排序,当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。
-
增量选择:先取一个小于 n n n 的增量 d 1 d_1 d1,把表中的全部记录分成 d 1 d_1 d1 组,所有距离为 d 1 d_1 d1 的倍数的记录放在同一组,在各组内进行直接插入排序;然后取第二个增量 d 2 < d 1 d_2 < d_1 d2<d1,重复上述过程,直到所取到的 d i = 1 d_i = 1 di=1,即所有记录已放在同一组中,再进行直接插入排序,由于此时已经具有较好的局部有序性,因此可以很快得到最终结果。
-
稳定性:当相同关键字的记录被划分到不同的子表时,可能会改变它们之间的相对次序,因此希尔排序是一种不稳定的排序算法。
-
适用性:希尔排序仅适用于顺序存储的线性表。
示例分析
希尔排序的效率
效率分析
- 希尔排序的效率很高,尽管它也属于插入排序的一种,但其高效的原因有两个:
- 对于直接插入排序来说,数据量越小,效率越高。
- 对于直接插入排序来说,数据基本有序时,效率越高。
空间复杂度
- 仅使用了常数个辅助单元,因此空间复杂度为 O ( 1 ) O(1) O(1)。
时间复杂度
-
增量序列的影响:希尔排序的时间复杂度依赖于所取“增量”序列的函数,这涉及一些数学上尚未解决的难题。
-
特定情况下的时间复杂度: 当增量序列为 d l t a [ k ] = 2 t − k + 1 − 1 dlta[k] = 2^{t-k+1} - 1 dlta[k]=2t−k+1−1 时,希尔排序的时间复杂度为 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2),其中 t t t 为排序趟数, 1 ≤ k ≤ t ≤ ⌊ log 2 ( n + 1 ) ⌋ 1 \leq k \leq t \leq \lfloor \log_2(n+1) \rfloor 1≤k≤t≤⌊log2(n+1)⌋。
-
实验基础上的推论:在大量的实验基础上推出:当 n n n 在某个特定范围内,希尔排序所需的比较和移动次数约为 n 1.3 n^{1.3} n1.3,当 n → ∞ n \to \infty n→∞ 时,可减少到 n ( log 2 n ) 2 [ 2 ] n(\log_2 n)^{2^{[2]}} n(log2n)2[2]。
-
最坏情况:在最坏情况下希尔排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
九、参考资料
鲍鱼科技课件
b站免费王道课后题讲解:
网课全程班: