排序算法:希尔排序

这篇具有很好参考价值的文章主要介绍了排序算法:希尔排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        1959 年 7 月,美国辛辛那提大学的数学系博士 Donald Shell 在 《ACM 通讯》上发表了希尔排序算法,成为首批将时间复杂度降到 O(n²)以下的算法之一。虽然原始的希尔排序最坏时间复杂度仍然是 O(n²) ,但经过优化的希尔排序可以达到 O(n^1.3)甚至O(n^7/6)。

        略为遗憾的是,所谓「一将功成万骨枯」,希尔排序和冒泡、选择、插入等排序算法一样,逐渐被快速排序所淘汰,但作为承上启下的算法,不可否认的是,希尔排序身上始终闪耀着算法之美。

        希尔排序本质上是对插入排序的一种优化,它利用了插入排序的简单,又克服了插入排序每次只交换相邻两个元素的缺点。它的基本思想是:

  • 将待排序数组按照一定的间隔分为多个子数组,每组分别进行插入排序。这里按照间隔分组指的不是取连续的一段数组,而是每跳跃一定间隔取一个值组成一组
  • 逐渐缩小间隔进行下一轮排序
  • 最后一轮时,取间隔为 1,也就相当于直接使用插入排序。但这时经过前面的「宏观调控」,数组已经基本有序了,所以此时的插入排序只需进行少量交换便可完成

举个例子,对数组 [84,83,88,87,61,50,70,60,80,99] 进行希尔排序的过程如下:

  • 第一遍(5间隔排序):按照间隔 5 分割子数组,共分成五组,分别是 [84,50],[83,70],[88,60],[87,80],[61,99]。对它们进行插入排序,排序后它们分别变成: [50,84],[70,83],[60,88],[80,87],[61,99],此时整个数组变成 [50,70,60,80,61,84,83,88,87,99]。
  • 第二遍(2间隔排序):按照间隔2分割子数组,共分成两组,分别是 [50,60,61,83,87],[70,80,84,88,99]。对他们进行插入排序,排序后它们分别变成:[50,60,61,83,87],[70,80,84,88,99],此时整个数组变成 [50,70,60,80,61,84,83,88,87,99]。这里有一个非常重要的性质:当我们完成 2 间隔排序后,这个数组仍然是保持 5 间隔有序的。也就是说,更小间隔的排序没有把上一步的结果变坏
  • 第三遍(1 间隔排序,等于直接插入排序):按照间隔1分割子数组,分成一组,也就是整个数组。对其进行插入排序,经过前两遍排序,数组已经基本有序了,所以这一步只需经过少量交换即可完成排序。排序后数组变成 [50,60,61,70,80,83,84,87,88,99],整个排序完成。

其中,每一遍排序的间隔在希尔排序中被称之为增量,所有的增量组成的序列称之为增量序列,也就是本例中的 [5,2,1]。增量依次递减,最后一个增量必须为1,所以希尔排序又被称之为「缩小增量排序」。要是以专业术语来描述希尔排序,可以分为以下两个步骤:

  • 定义增量序列 D m>D m−1>D m−2>...>D 1 =1
  • 对每个 Dk​ 进行 「Dk​ 间隔排序」

有一条非常重要的性质保证了希尔排序的效率:

  • 「D k+1间隔」 有序的序列,在经过 「D k间隔」 排序后,仍然是 「D k+1间隔」 有序的

        增量序列的选择会极大地影响希尔排序的效率。本例中,我们采用的增量序列为 
D m=N/2,D k =D k+1/2,这个序列正是当年希尔发表此算法的论文时选用的序列,所以也被称之为希尔增量序列。代码实现如下:

public static void shellSort(int[] arr) {
    // 间隔序列,在希尔排序中我们称之为增量序列
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        // 分组
        for (int groupStartIndex = 0; groupStartIndex < gap; groupStartIndex++) {
            // 插入排序
            for (int currentIndex = groupStartIndex + gap; currentIndex < arr.length; currentIndex += gap) {
                // currentNumber 站起来,开始找位置
                int currentNumber = arr[currentIndex];
                int preIndex = currentIndex - gap;
                while (preIndex >= groupStartIndex && currentNumber < arr[preIndex]) {
                    // 向后挪位置
                    arr[preIndex + gap] = arr[preIndex];
                    preIndex -= gap;
                }
                // currentNumber 找到了自己的位置,坐下
                arr[preIndex + gap] = currentNumber;
            }
        }
    }
}

        这份代码与我们上文中提到的思路是一模一样的,先分组,再对每组进行插入排序。同样地,这里的插入排序也可以采用交换元素的方式。

        实际上,这段代码可以优化一下。我们现在的处理方式是:处理完一组间隔序列后,再回来处理下一组间隔序列,这非常符合人类思维。但对于计算机来说,它更喜欢从第 gap 个元素开始,按照顺序将每个元素依次向前插入自己所在的组这种方式。虽然这个过程看起来是在不同的间隔序列中不断跳跃,但站在计算机的角度,它是在访问一段连续数组。

public static void shellSort(int[] arr) {
    // 间隔序列,在希尔排序中我们称之为增量序列
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        // 从 gap 开始,按照顺序将每个元素依次向前插入自己所在的组
        for (int i = gap; i < arr.length; i++) {
            // currentNumber 站起来,开始找位置
            int currentNumber = arr[i];
            // 该组前一个数字的索引
            int preIndex = i - gap;
            while (preIndex >= 0 && currentNumber < arr[preIndex]) {
                // 向后挪位置
                arr[preIndex + gap] = arr[preIndex];
                preIndex -= gap;
            }
            // currentNumber 找到了自己的位置,坐下
            arr[preIndex + gap] = currentNumber;
        }
    }
}

        经过优化之后,这段代码看起来就和插入排序非常相似了,区别仅在于希尔排序最外层嵌套了一个缩小增量的 for 循环;并且插入时不再是相邻数字挪动,而是以增量为步长挪动。文章来源地址https://www.toymoban.com/news/detail-682375.html

到了这里,关于排序算法:希尔排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 排序算法——希尔排序图文详解

    注1:本篇是基于对直接插入排序法的拓展,如果对直接插入法不了解,建议先看看 直接插入排序 注2:本篇统一采用升序排序 希尔排序法又称缩小增量法。 希尔排序其实是直接插入排序的改进。 其 基本思想是 : 先选定一个整数gap,把待排序文件中所有记录分成数组,所有

    2024年02月07日
    浏览(35)
  • 排序算法-插入/希尔排序

    直接插入排序是一种简单的插入排序法,其基本思想是: 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。 当插入第i(i=1) 个元素时,前面的 array[0],array[1],…,array[i-1] 已经排好序,此时用

    2024年02月05日
    浏览(42)
  • 排序算法-----希尔排序

    目录 前言 希尔排序(shell) 排序原理 大致思路 示例  代码实现(C语言) 算法分析 时间复杂度 空间复杂度 稳定性         前面我有一篇插入排序的详细的文章讲解(链接:排序算法-----插入排序(图文详解)_灰勒塔德的博客-CSDN博客)今天我们接着学习排序算法中的希尔

    2024年02月09日
    浏览(32)
  • 排序算法之【希尔排序】

      📙 作者简介:  清水加冰,目前大二在读,正在学习C/C++、Python、操作系统、数据库等。 📘 相关专栏: C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误还望各路大佬指正! ✨每一次努力

    2024年02月08日
    浏览(38)
  • 【排序算法】希尔排序

    希尔排序是一种经典的排序算法,它通过多次插入排序的方式,以及逐步缩小增量的策略,实现对数据的高效排序,希尔排序法又称缩小增量法。 希尔排序的核心思想在于将待排序的数据分成若干组,对每一组数据进行插入排序。这样做的好处是,一方面可以减少数据的比较

    2024年04月13日
    浏览(31)
  • 八大排序算法之插入排序+希尔排序

    目录 一.前言(总体简介) 关于插入排序  关于希尔排序: 二.插入排序 函数首部: 算法思路: 算法分析 插入排序代码实现: 插入排序算法的优化前奏:  三.希尔排序(缩小增量排序) 1.算法思想:  2.算法拆分解析  序列分组 分组预排序: 分组预排序的另一种实现方式: 希尔排序的实现

    2023年04月09日
    浏览(43)
  • 排序算法:插入排序(直接插入排序、希尔排序)

    朋友们、伙计们,我们又见面了,本期来给大家解读一下有关排序算法的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏: C语言:从入门到精通 数据结构专栏: 数据结构 个  人  主  页 : stackY、 目录   前言: 1.排序

    2024年02月09日
    浏览(48)
  • 【排序算法】希尔排序详解!(源码+实现)

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 算法—排序篇 🌄 莫道桑榆晚,为霞尚满天! 什么是希尔排序?希尔排序是如何实现的?它的思想和由来又是什么? 本文将从多方面,精细的带你了解希尔排序,让你彻底掌握它! 希尔排序(Shell Sort)是一种排序算法,由美国计算机

    2024年02月05日
    浏览(35)
  • 【排序算法】希尔排序!(保姆级教学)

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 算法—排序篇 🌄 莫道桑榆晚,为霞尚满天! 什么是希尔排序?希尔排序是如何实现的?它的思想和由来又是什么? 本文将从多方面,精细的带你了解希尔排序,让你彻底掌握它! 希尔排序(Shell Sort)是一种排序算法,由美国计算机

    2024年02月08日
    浏览(39)
  • 排序算法 —— 希尔排序(图文超详细)

    排序算法: 1、直接插入排序 2、选择排序 3、堆排序 希尔排序是将数据分组,将每一组进行插入排序。 每一组排成有序后,最后整体就变有序了。 上图中gap为5,说明要分成5组。 这5组分别用了五种颜色的线条连接起来了。 第1组:9、4 第2组:1、8 第3组:2、6 第4组:5、3 第

    2024年02月05日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包