什么是希尔排序?

本文首发自「慕课网」,想了解更多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圈优质内容,分享干货知识,帮助你成为更好的程序员!

你可能感兴趣的:(程序员)