“希尔排序:打破时间瓶颈的排序算法 “

🔍什么是希尔排序

希尔排序(Shell Sort)是插入排序的一种高效率的改进版本,也称为缩小增量排序。它基于插入排序,但使用了不同的增量序列,通过将序列分成若干个子序列进行插入排序,逐步减小增量,最终完成排序。

希尔排序法的基本思想是:先选定一个整数,把待排序的数据所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。​ 每次排序让数组接近有序的过程叫做预排序,最后一次等于1时插入是直接插入排序。


🔑希尔排序分组思想

在这里插入图片描述

  1. 我们首先从间隔为5分组,间隔为gap分为一组,总计gap组,多组并排
    在这里插入图片描述

gap代表间隔,此时为我们已经分好组了

第一组: 9 4
第二组: 1 8
第三组: 2 6
第四组: 5 3
第五组: 7 5

我们对这几组分别进行排序分别得到:
在这里插入图片描述

  1. 我们缩小gap增量,使的变成2

在这里插入图片描述
此时为我们分组是

第一组: 4 2 5 8 5
第二组: 1 3 9 6 7

我们对这两组分别进行排序分别得到:
在这里插入图片描述

  1. 缩小gap增量,使的变成1,变成1就是已经变成插入排序了。到这里已经是最后一步了。
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/58c71fb2a4c9479fa93bc927f6e7eb72.png

gap >1 就是预排序
gap越大,大的数可以更快的到后面,小的数可以更快的到前 越不接近有序
gap越小,大的小的挪动越慢,但是他越接近有序
gap会逐渐缩小,间隔也会逐渐缩小。
整体的数据会更加趋于有序,这个时候使用直接插入排序效率会更高。
gap == 1,就是直接插入排序


📈希尔排序的优缺点

希尔排序是基于插入排序的一种排序算法,它通过将一串待排序的数据分成多个子序列来实现排序,最终将这些子序列合并成一个有序序列。相比插入排序,希尔排序的优缺点如下:

  • 优点
  1. 相对于其他简单的排序算法(如冒泡排序、选择排序等),希尔排序的时间复杂度较低,尤其是对于大规模数据的排序。

  2. 希尔排序是一种原地排序算法,不需要额外的存储空间。

  3. 由于希尔排序改变了插入排序中元素的位置,从而减少了元素之间的比较和交换次数,因此希尔排序在实际应用中展现出了较高的排序效率。

  • 缺点
  1. 希尔排序的时间复杂度与增量(gap)序列有关,不同的增量(gap)序列会影响排序的效率。
  2. 希尔排序是一种不稳定的排序算法,相同的元素在排序后可能会改变相对位置。

👨‍💻希尔排序代码剖析

  • 确定增量序列gap

    1. 先把gap给成数组的长度
      int gap = n
    2. 确定 增量序列gap
      gap=gap/3+1 +1可以保证最后一次一定是1 即最后一次是直接插入排序
  • 确定循环条件外层循环条件
    while (gap > 1)该while循环的作用是随着跨度gap的不断减小,进行希尔排序的每一轮迭代,直到跨度为1时结束循环,完成希尔排序。

  • 确定循环控制每组数据进行插入排序的过程

    for (int i = 0; i < n - gap; ++i) n - gap表示数组a中第一组数据的最后一个元素下标。在希尔排序中,我们将数组n个元素分成若干组(组与组之间的元素下标相差gap),每组中的元素个数通常为gap。因此,在对每组数据进行插入排序时,需要在第一组数据中从第一个元素开始进行比较,直到最后一个元素。由于每组数据中的元素下标相差gap,所以第一组数据的最后一个元素下标为n-gap。因此,for循环的条件判断语句可以写为 i < n - gap。

  • 使用两个变量确定当前组需要排序的范围和下一个待插入的元素。
    int end = i
    int tmp = a[end + gap]

  • 里层循环不断向后更新位置
    (end >= 0)不断查找当前元素 a[end] 在以 end-gap 为结尾的子序列中的正确位置,并将其插入到正确的位置中,直到 end 的值小于 0 为止

  • 判断 元素是否比待插入元素 tmp 大
    if (a[end] > tmp)
    {
    a[end + gap] = a[end]
    end-=gap 需要往后移动一个 gap 的位置,以便为下一个待插入元素腾出位置
    }

  • 实现待插入操作
    a[end + gap] = tmp 在这之前,我们通过 a[end + gap] = a[end]; 操作,已经将子序列中所有大于待插入元素的元素往后移动了一个 gap 的位置,以腾出正确的插入位置。现在,我们已经找到了待插入元素的正确位置,也就是以 end-gap 为结尾的子序列中的某个位置。因此,直接将待插入元素(tmp)赋值给 a[end+gap],实现了在子序列中插入待插入元素的目的。

void ShellSort(int* a, int n)
{
    int gap = n;
    while (gap > 1)  // 当跨度为1时,就是普通的插入排序,不需要再进行希尔排序了
    {
    // 通过除以3再加1的方式来缩小跨度 +1可以保证最后一次一定是1,
    //这是希尔排序中常用的增量序列
        gap = gap / 3 + 1;  
        for (int i = 0; i < n - gap; ++i)  // 对每组数据进行插入排序
        {
            int end = i;  // end表示每组数据的最后一个元素的下标
            int tmp = a[end + gap];  // tmp表示当前组中下一个待插入的元素
            while (end >= 0)  // 在当前组中,从后往前找到tmp应该插入的位置
            {
                if (a[end] > tmp)  // 如果当前位置的元素比tmp大,就将该元素后移gap个位置,为tmp腾出位置
                {
                    a[end + gap] = a[end];
                    end -= gap;  // 继续向前查找
                }
                else  // 如果当前位置的元素比tmp小,就找到了tmp应该插入的位置
                {
                    break;
                }
            }
            a[end + gap] = tmp;  // 将tmp插入到当前组的合适位置
        }
    }
}

在上面的代码中,我们可以看到,希尔排序主要包含两个阶段:预排序阶段和插入排序阶段。其中,预排序阶段是通过不断缩小跨度来实现的,而插入排序阶段则是对每组数据进行插入排序。在预排序阶段中,我们使用了一个常用的增量序列: gap=gap/3+1,这个增量序列可以保证最后一次的跨度一定是1,从而保证了最后一次排序是普通的插入排序。

在插入排序阶段中,我们首先定义了一个变量end,表示每组数据的最后一个元素的下标。然后,我们将当前组中下一个待插入的元素存储在了tmp变量中。接着,我们从后往前遍历当前组中的元素,将比tmp大的元素后移gap个位置,为tmp腾出位置。最后,我们将tmp插入到当前组的合适位置。

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值