8.2.插入排序
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;
}
}
算法特性:
-
数组已经有序时,最好时间复杂度为\(O(n)\),当数组完全逆序时,最坏时间复杂度为\(O(n^{2})\),平均时间复杂度为\(O(n^{2})\)。
-
空间复杂度为\(O(1)\),原地排序,插入排序是稳定的排序算法,插入排序对于部分有序的数组非常高效。
-
直接插入排序可以用与顺序存储和链式存储的线性标,折半插入排序只能用于顺序存储的线性表。
常见问题:
-
如何优化插入排序?
- 可以使用二分查找来确定插入元素的位置,减少了查找次数,但是交换次数仍然不变,所以优化效果不是非常明显。
-
插入排序适用于什么场景?
- 插入排序适用于,数据量比较小,或者数组元素已经部分有序的数组,因为插入排序的常数因子较小,实现简单,在小规模数据上表现良好。
- 插入排序适用于,数据量比较小,或者数组元素已经部分有序的数组,因为插入排序的常数因子较小,实现简单,在小规模数据上表现良好。
2. 希尔排序
核心思想:
-
希尔排序是插入排序的改进版本。
-
希尔排序的核心思想在于将数组分成多个子序列来进行插入排序,子序列不是由连续的元素构成的,而是相隔特定增量的元素构成的。
-
对于每个子序列进行插入排序。
-
排序完成之后,缩小增量重复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;
}
}
}
算法特性:
-
希尔排序的时间复杂度依赖于增量序列的选择,所以时间复杂度分析比较困难,当n在某个范围时,希尔排序的时间复杂度约为\(O(n^{1.3})\),最坏情况下的时间复杂度为\(O(n^{2})\)。
-
空间复杂度为\(O(1)\),原地排序,不稳定排序算法,对于部分有序的数组有较好的性能,能够充分利用插入排序在处理基本有序的数组时效率较高。
-
希尔排序只能适用于顺序存储的线性表。
常见问题:
-
希尔排序为什么比插入排序的效率高?
- 希尔排序通过设置不同的增量,使得数组元素快速移动到离其最终位置较近的地方,使得数组接近有序。当增量为1的时候,执行的就是普通插入排序,此时数组基本有序,所以插入排序的效率大大提升,所以整体效率比直接使用插入排序要高。
-
代码中增量变化的基本规则是怎样的,有没有最优的增量序列?
-
常见增量变化的规则是,初始增量为数组长度的一半,每次将增量得到新增量。
-
目前还没有找到理论上最优的增量序列。
-

浙公网安备 33010602011771号