经过大量测试,希尔排序的平均时间复杂度:O(N*1.3),或者O(NlogN),这就非常牛逼了,发明这个算法的作者简直是个天才!最优时间复杂度:O(N),即已经有序。
希尔排序和插入排序类似,都是将前面的区间看作有序,然后比较插入。其实准确的说,插入排序是希尔排序的一种特例(gap == 1)。
先给大家来一个动态图,理解理解。
下边放几个抽象图,大家仔细理解一下,gap的作用就在下面体现出来。
希尔排序只是将gap作为一个变化量,逐渐向1逼近,每次gap的值都来执行一遍插入排序。
上代码:
void ShellSort_incline(int* arr, int n)
{
//确定初识gap,将gap初始化到最大n,以便于后面gap能从最大缩小到最小
int gap = n;
while (gap > 1)
{
//gap逐渐缩小
//gap/3 +1是为了确保最后gap能达到1
//也可以gap = gap / 2,gap怎么变都可以
gap = gap / 3 + 1;
//i从0 --> n-gap-1,每次比较相距gap的两个数
for (int i = 0; i < n - gap; i++)
{
//下面就是和插入排序一样的,只不过比较的是相距为gap的位置
//其实插排就是希尔排序的特殊情况,即gap = 1
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
那么就会有人问了:既然gap最终都会等于1,等于1的时候那不就是插入排序了吗,为啥要多此一举呢,时间复杂度不会更高吗?
答案是有必要:因为从代码上来看,当前一个数比后一个数小的时候,便直接break结束循环。那么gap从大到小执行比较插入数据时,会将无序的数组,逐渐变得大致有序,并非完全有序。那么最后gap逐渐趋于一的时候,因为数组已经大致有序,变有很大可能会直接执行break,不会反复循环,而由于gap比较大的时候,两个需要比较数之间又间隔很大,那么每次比较插入就不会执行n次,而是会执行一个取决于gap的大小的次数。
即总的来说:gap很大时,每一趟执行的次数少,而当gap很小时,有很可能会直接执行break,执行的次数也很小。综合来说,实质上,希尔排序是对插入排序的一种优化。
点个赞再走!!!不然不发了!!!