希尔排序算法

介绍

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),也称递减增量排序算法,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

要点

  • 分组增量要初始需要为数组的一半,后续每次减半,需要使用for循环实现
  • 分组进行插入排序,需要使用while进行循环
  • 所谓的分组,其实就是循环的时候隔着固定的增量而已,也就是类似于 i + 2 这种 等差数列

代码

public static int[] arr = new int[] { 64, 13, 57, 11, 27, 93, 34, 98, 78, 50 };
public static void InsertSort()
{
    int length = arr.Length;
    int temp;
    // step初始为5,然后只要满足step >= 1的条件,就开始走循环内部,循环内部走完,再走ste /= 2
    // 这样就能控制step增量每次减半,然后直到增量为1
    for (int step = length / 2; step >= 1; step /= 2)
    {
        // 从i=step开始,依次增加
        for (int i = step; i < length; i++)
        {
            // 暂时记录arr[i]的数值
            temp = arr[i];
            // 因为要跨增量分组进行插入排序, 所以把i和i-step的分为一组,然后两者进行插入排序
            // 比如i=5会和i=0的为一组,这个分组会受增量的控制
            int j = i - step;
            // 这里是重点,这里会将分组的数据进行插入排序,比如j = 4,step= 2
            // 如果满足arr[4] > arr[6]以及j>=0, 那么先会将arr[6]等于arr[4]
            // 然后将j 减去 step,就是4 - 2 = 2,又会将arr[2]和arr[6]进行对比,满足要求就继续下去,那么arr[4]就等于arr[2]
            // 以此类推,直到不满足要求为止
            while (j >= 0 && arr[j] > temp)
            {
                arr[j + step] = arr[j];
                j -= step;
            }
            // 这时候j = -2, 因为arr[4]和arr[6]都比temp大,所以最后需要把arr[0]的值改成temp,
            arr[j + step] = temp;
        }
        Print(step);
    }  
}

运行过程打印如下:

增量为564, 13, 57, 11, 27, 93, 34, 98, 78, 50,
增量为227, 11, 34, 13, 57, 50, 64, 93, 78, 98,
增量为111, 13, 27, 34, 50, 57, 64, 78, 93, 98,

总结

为什么会有人说希尔排序算是对插入排序的一种优化。我接下来展示两串代码:
这个是作者之前的插入排序一文当中的一部分代码,暂且称作CR1

for (j = i-1; j >= 0 && temp < array[j]; j--)
{
    // 将比temp大的数集体往后移动
    array[j + 1] = array[j];
}
// 最后找到比temp小的数,然后往这个数的后面一个下标插入temp数
array[j + 1] = temp;

原文链接

接下来我们看看本文当中的一部分代码,我们称作CR2:

int j = i - step;
while (j >= 0 && arr[j] > temp)
{
    arr[j + step] = arr[j];
    j -= step;
}
arr[j + step] = temp;

那么我们把这个代码换一种写法,称作CR3,大家看看:

int j = 0;
for (j = i - step; j >= 0 && temp < arr[j]; j -= step)
{
    arr[j + step] = arr[j];
}
arr[j + step] = temp;

这回是不是瞬间一目了然了,CR1和CR3何其的相似。,这两部分都是插入排序,也都是精髓点,两者都是在设定为有序序列里面查找temp应该放的位置,也就是满足arr[j] < temp && arr[j+2] > temp条件的地方,而这个地方就是arr[j+1]或者是arr[j+step],然后把temp的值,也就是arr[i]的值直接插入到这个位置,成功的放入有序序列。

所以以后遇到说有用到插入排序方法的代码,八九不离十的离不开这种写法,大概的代码样式就是如下:

for(初始化 j, 设置j的限制条件(比如j>0&&temp<arr[j]), 设置j的递减/递增的间隔数)
{
    arr[j+/-间隔数] = arr[j];
}
arr[+/-间隔数] = temp

希尔排序总的来说,算是比较难写出来的,思路好懂,但是不一定写的出来。尤其是当遇到分组的时候,我第一次听到分组,下意思的就会去想用一个新的数组来记录,然后再把这个数组进行排序。但是希尔排序是内部排序的算法,也就是不需要动用新数组的,这也是希尔排序空间复杂度为**O(1)**的原因。

目前作者只能总结出这么多,可能因为作者技术有限,后面遇到再更新吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值