什么是希尔排序?

二进制
• 阅读 588

本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!

作者| 慕课网精英讲师 JdreamZhang

希尔排序(Shell Sort),是计算机科学与技术领域中较为简单的一种排序算法。

希尔排序是插入排序的一种,有时候也被称为 “缩小增量排序”。它是插入排序的改进版,与插入排序的不同之处在于,希尔排序会优先比较距离较远的元素。希尔排序是按照其设计者希尔(Donald Shell)的名字命名而来,并于 1959 年公布出来。

  1. 希尔排序过程
    在介绍完希尔排序之后,我们一起来看一下希尔排序的实现步骤具体是什么样的吧。这里我们假设待排序的序列为 [9,2,11,7,12,5],我们按照从小到大的序列进行排序。

1.1 算法步骤
步骤 1:
选择一个增量序列 k1,k2, … km,其中 k1>k2>…km=1,即增量序列大小依次减小,并且最后一个增量序列大小为 1。

步骤 2:
按照增量序列的个数 m,对整个待排序序列进行 m 趟排序。

步骤 3:
每一趟排序,根据对应的增量 ki,需要将待排序的序列分成对应长度的子序列,分别在子序列上面进行直接插入排序。当且仅当增量序列为 1 时,整个序列作为一个整体处理。

其实,上面的步骤 1 和 步骤 2 都是在排序之前进行的处理,选择对应的增量。上面的 步骤 3 每执行一次,就相当于是进行了一次插入排序,只是每次都会选择一个增量,将整个待排序序列按照增量进行划分,然后在对应增量上面进行插入排序。接下来,让我们用上面的待排序数字队列 [9,2,11,7,12,5] 进行整个算法步骤的排序演示工作。

1.2 算法演示
按照 2.1 节的排序步骤,我们需要先选择对应的希尔排序中的增量值,按照一般性的原则,我们可以将增量按照待排序的序列长度依次整除 2,直到增量为 1 停止,得到对应的增量。如下:

6/2 = 3, 3/2 = 1 //依次获得增量值3,1,除法取整
代码块1
接着,我们调用 2.1 中的步骤 2, 步骤 3,按照增量值的取法,依次进行对应序列的插入排序,首先我们取增量值为 3,对应排序示例如下:

[9,2,11,7,12,5] --> [9,7],[2,12],[11,5] //增量取3,整个待排序序列按照增量为3进行分组,分成3组,依次排序
[9,7],[2,12],[11,5] --> [7,9],[2,12],[5,11] //对应增量位置进行排序
[7,9],[2,12],[5,11] --> [7,2,5,9,12,11] //完成第一次的对应增量的排序过程
代码块123
Tips: 希尔排序过程中会每次选择一个增量值,然后将待排序列表按照增量值进行分组,对应的分组内部进行插入排序。

在完成增量为 3 的插入排序之后,我们接着进行增量为 1 的插入排序,这个步骤其实跟我们之前的插入排序步骤完全一致。整个过程如下:

[7,2,5,9,12,11] --> [7];[2,5,9,12,11] //插入排序初始化,选择第一个元素作为排序好的序列
[7];[2,5,9,12,11] --> [2,7];[5,9,12,11] //选择未排序元素2,插入排序好的序列[7]形成新的排序好序列[2,7]
[2,7];[5,9,12,11] --> [2,5,7];[9,12,11] //选择未排序元素5,插入排序好的序列[2,7]形成新的排序好序列[2,5,7]
[2,5,7];[9,12,11] --> [2,5,7,9];[12,11] //选择未排序元素9,插入排序好的序列[2,5,7]形成新的排序好序列[2,5,7,9]
[2,5,7,9];[12,11] --> [2,5,7,9,12];[11] //选择未排序元素12,插入排序好的序列[2,5,7,9]形成新的排序好序列[2,5,7,9,12]
[2,5,7,9,12];[11] --> [2,5,7,9,11,12] //选择未排序元素11,插入排序好的序列[2,5,7,9,12]形成新的排序好序列[2,5,7,9,11,12]
代码块123456
Tips: 增量为 1 时候执行 步骤 3,实际上就是执行一次插入排序,只是在执行插入排序之前,之前的序列在一定程度上面已经进行了插入排序,所以在增量为 1 的插入排序过程中可以减少插入时候比较的次数,从而可以在一定程度上面减少算法的运行时间。

从上面的示例可以看出,其实整个希尔排序的过程,就是根据增量大小依次进行插入排序,本质上还是针对插入排序的一种优化。

  1. Java 代码实现
    在说明希尔排序的整个过程之后,接下来,我们看看如何用 Java 代码实现希尔排序算法。

import java.util.Arrays;

public class ShellSort {

public static void main(String[] args) {

    //初始化需要排序的数组
    int array[] = {9, 2, 11, 7, 12, 5};
    //初始化希尔排序的增量为数组长度
    int gap = array.length;
    //不断地进行插入排序,直至增量为1
    while (true) {
        //增量每次减半
        gap = gap/2;
        for (int i = 0; i < gap; i++) {
            //内部循环是一个插入排序
            for (int j = i + gap; j < array.length; j += gap) {
                int temp = array[j];
                int k = j - gap;
                while (k >= 0 && array[k] > temp) {
                    array[k + gap] = array[k];
                    k -= gap;
                }
                array[k + gap] = temp;
            }
        }
        //增量为1之后,希尔排序结束,退出循环
        if (gap == 1)
            break;
    }
    //打印出排序好的序列
    System.out.println(Arrays.toString(array));
}

}
代码块1234567891011121314151617181920212223242526272829303132333435
运行结果如下:

[2, 5, 7, 9, 11, 12]
代码块1
代码中的第 8 行初始化一个需要排序的数组,后面按照从小到大的排序规则,实现了数组的排序。第 12 行至 30 行是整个希尔排序的流程。第 14 行代码表示希尔排序中的增量每次整除 2 取得,第 17 行至 25 行是一个 for 循环结构,表明按照增量进行插入排序。最后第 32 行代码输出排序好的数组。

  1. 小结
    本篇文章为大家介绍了希尔排序算法,需要熟悉希尔排序的算法流程,知道希尔排序算法的实现思路的小伙伴,可以自己用代码实现希尔排序算法。

欢迎关注「慕课网」,发现更多IT圈优质内容,分享干货知识,帮助你成为更好的程序员!

点赞
收藏
评论区
推荐文章
22 22
4年前
【排序算法动画解】排序介绍及冒泡排序
本文为系列专题的第12篇文章。1.2.3.4.5.6.7.8.9.10.11.本文先简单介绍一下什么是排序,然后再结合动画介绍暴力排序和冒泡排序。1.什么是排序?排序在日常生活中无处不在。比如考试成绩的排名、体育课的从低到高的队形、网购时按价格升序排列或降序排列等等。|姓名|学号|班级|成绩|||||
Wesley13 Wesley13
3年前
java中Comparatable接口和Comparator接口的区别
1、不同类型的排序规则.自然排序是什么?  自然排序是一种升序排序。对于不同的数据类型,升序规则不一样:  BigDecimalBigIntegerByteDoubleFloatIntegerLongShort类型,是按照数值的大小进行排序的。例如:12<23,111.1113.23  Charac
威尔we 威尔we
4年前
golang 之快速排序
1、快速排序稳定性快速排序是不稳定的算法,它不满足稳定算法的定义。算法稳定性假设在数列中存在aiaj,若在排序之前,ai在aj前面;并且排序之后,ai仍然在aj前面。则这个排序算法是稳定的!2、快速排序
22 22
4年前
【排序算法动画解】直接插入排序
本文为系列专题的第14篇文章。1.2.3.4.5.6.7.8.9.10.11.12.13.前面介绍了已经介绍了三种排序,暴力排序、冒泡排序和简单选择排序,一个共同点都是基于交换。我们可以用另一种视角来看待排序,即将一个待排序的数组看成两个部分:有序区和乱序区。在排序开始前,整个数组都是乱序区,而有序区则为空:排序开始后,有序区
徐小夕 徐小夕
4年前
程序员必备的几种常见排序算法和搜索算法总结
前言最近为了巩固一下自己的算法基础,又把算法书里的基本算法刷了一遍,特地总结一下前端工程师需要了解的排序算法和搜索算法知识,虽然还有很多高深算法需要了解,但是基础还是要好好巩固一下的.本文将以图文的形式为大家介绍如下算法知识,希望在读完之后大家能有所收获:冒泡排序及其优化选择排序插入排序归并排序快速排序顺序搜索二分搜索
Wesley13 Wesley13
3年前
Java面试总结(排序算法)
1.冒泡排序算法描述:两两比较,大的放后面2.选择排序算法描述:在m元数组中找到最小值的位置,然后将最小值的位置和第n(n0,1,2,....m1)位的值对调,排序k次则m元数组中前k(k<m)位的值已经排序好,m元数组中前k位的值不需要再进行排序,此时需要排序的元素只有mk个3.插入排序算
Stella981 Stella981
3年前
Shell(希尔)排序
Shell(希尔)排序    对于直接插入排序而言,如果一个很小的数据单元位于很靠近右端的位置上,为了把这个数据单元移动到左边正确的位置上,中间所有的数据单元都需要向右移动一格。这个步骤对每一个数据项都执行了近n次复制。虽然不是所有的数据项都必须移动n个位置,但平均下来,每个数据项都会移动n/2格,总共是n\n/2次复制。因此,直接插入
Wesley13 Wesley13
3年前
279,对链表进行插入排序
对链表进行插入排序。!(https://oscimg.oschina.net/oscnet/9dfd592075eb9c212bf1eabe9e8ecb60522.gif)插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
Stella981 Stella981
3年前
Lua 排序算法
冒泡排序(BubbleSort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。算法步骤1.有一个长度为n
菜园前端 菜园前端
2年前
什么是冒泡排序
原文链接:什么是冒泡排序(bubbleSort)?冒泡排序是所有排序算法中最简单的一种,当然也是性能最差的一种。冒泡排序的思想其实很简单,就如它的名字一样在水中"冒泡"。水中有很多散乱的小气泡,然后一个个气泡往水面上冒出。例如一组无序的数组,最左边就是水底
菜园前端 菜园前端
2年前
什么是插入排序?
原文链接:什么是插入排序(insertionSort)?在数组中从左到右依次取一个数出来,然后把它放到合适的位置。从思想上可以分为有序区和无序区,有序区在左边代表已经排列好的元素。算法步骤1.默认左边第一个元素已经在有序区了2.在无序区取一个数出来(第二个