十大经典排序算法-希尔排序


前言

本文重点讲解十大经典排序算法的希尔排序

要点

希尔排序是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本,该方法因 D.L.Shell 于 1959 年提出而得名。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:

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

基本思想

该方法实质上是一种分组插入方法。
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

代码实现

以下代码由java实现

public class ShellSort {

    /**
     * 希尔排序
     * @param arr
     * @param n
     * @return
     */
    public static int[] sort(int[] arr, int n) {
        if (n <= 0) {
            return null;
        }
        // 初始化参数
        int key, i, j;
        // 初始增量 inc , 每趟过后 inc 除以 2
        for (int inc = n / 2; inc > 0; inc = inc / 2) {
            // 每一趟采用插入排序
            for (i = inc; i < n; i++) {
                // 保存当前key
                key = arr[i];
                // 当前key要比上一个元素小才交换
                for (j = i; j >= inc && key < arr[j - inc]; j -= inc) {
                    arr[j] = arr[j - inc];
                }
                // 注意 j -= inc
                arr[j] = key;
            }
        }
        return arr;
    }

    public static void main(String[] args) {
        int[] arr = {9, 5, 3, 6, 8, 1, 2, 3, 11, 52, 32, 11, 23};
		System.out.println(Arrays.toString(sort(arr, arr.length)));
    }
}

时间性能

  1. 增量序列的选择
    Shell排序的执行时间依赖于增量序列。
    好的增量序列的共同特征:
  • 最后一个增量必须为1;
  • 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
  • 有人通过大量的实验,给出了较好的结果:当n较大时,比较和移动的次数约在$n^1.25$到$(1.6n)^1.25$之间。
  1. Shell排序的时间性能优于直接插入排序
    希尔排序的时间性能优于直接插入排序的原因:
  • 当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
  • 当n值较小时,n和n^2的差别也较小,即直接插入排序的最好时间复杂度$O(n)$和最坏时间复杂度$0(n^2)$差别不大。
  • 在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。

空间复杂度

希尔排序的空间复杂度为常数阶$O(1)$。

稳定性

希尔排序算法是不稳定的。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变。


文章作者: Baymax
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Baymax !
评论
  目录