一、前言
直接插入存在优化:可以通过对已排序好的序列,通过二分的方法,来找到待插入值的合适位置。
二分:
核心思路:每次砍掉一半范围,快速缩小答案区间,效率极高。
二、希尔排序(缩小增量排序)-》对直接插入排序的优化
1.核心:用到了缩小增量数组
2.直接插入排序的特点:
(1)数据也有序,整体排序效率越高。
(2)当n比较小的时候,n^2也不会很大-》时间复杂度 直接插入排序最理想的待排序序列是已经排序好的序列,所以时间复杂度就是O(n) 最糟糕的状态就是待排序序列是完全倒序,所以时间复杂度就是O(n^2)。
3.希尔排序的逻辑:
先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中记录"基本有序"时,在对全体记录进行一次直接插入排序。
跳着分组:
①第一次以增量为5为例,每个颜色对应的数据是一组。然后对这每一组单独进行直接插入排序。

不用排 10 21 50 本身有序。
按照刚刚方法完全排序完的序列:

②第二次以增量3为例,每个颜色对应的数据是一组。然后对这每一组单独进行直接插入排序。

以增量为3排序完的序列完全排序完的结果是

③第三次按照增量1排序,每个颜色对应的数据是一组。然后对这每一组单独进行直接插入排序。

以增量为1排序完的序列完全排序完的结果是

此时,最终只要按照增量1处理之后,会完全有序
3.缩小增量数组的取值好坏和希尔排序的时间复杂度直接相关,但又没有一个最优解。
但他有一些规则:
(1)取值从大到小
(2)取值尽量互素
(3)最后一个增量必须是1
4.希尔排序的特点:子序列的构成不是简单的"逐段分割",而是将相隔某个"增量"的记录组成一个子序列。
5.时间复杂度:
希尔排序的分析是个复杂的问题,因为它的时间是所取"增量"序列的函数,这涉及一些数学上尚未解决的难题。通俗的来说他的时间复杂度是动态变化的,到目前为止尚未有人求得一种最好的增量序列。增量序列可以有各种取法,但需注意:应使增量序列中的值没有除1之外的其他公因子也就是说他是素数,并且最后一个增量值必须等于1.
三、代码实现
方法一:以增量3为例

此时我们先处理红色组,再处理绿色组,最后处理蓝色组
此时有效值的处理顺序为:
红1->红2->红3->红4->红5->绿1->绿2->绿3->绿4->绿5->蓝1->蓝2->蓝3->蓝4->蓝5
时间复杂度是O(n^3).
方法二:
不再处理完一个组,再处理下一个组,而是每一个组一次只处理一个值,然后轮换,直到所有的值处理完毕。
全部此时有效值的处理顺序为:
红2->绿2->蓝2->红3->绿3->蓝3->红4->绿4->蓝4->红5->绿5->蓝5
代码实现:
cs
void Shell(int arr[], int len, int gap_val) {
//此时增量为5;
//需要将每一个执行插入排序
for (int i = gap_val;i < len;i++) {//此时i控制的需要往前插入的待排序值 gap_val前面的值都是每一组的第一个元素,第一个元素不用排
int tmp = arr[i];
int j = i - gap_val;
for (;j >= 0;j -= gap_val) {
if (arr[j] > tmp) {
arr[j + gap_val] = arr[j];
}
else {
break;//遇到了不大于我的值,停下来把值放回去
}
}
//越界了,停下来,把值放回去
arr[j + gap_val] = tmp;
}
}
void Shell_Sort(int arr[], int len) {
int Gap[] = { 5,3,1 };
int len_Gap = sizeof(Gap) / sizeof(Gap[0]);
for (int i = 0;i < len_Gap;i++) {//趟数
Shell(arr,len,Gap[i]);
}
}