• 排序算法总结之希尔排序


    一,希尔排序算法介绍

    ①希尔排序又称缩小增量排序 ,它本质上是一个插入排序算法。为什么呢?

    因为,对于插入排序而言,插入排序是将当前待排序的元素与前面所有的元素比较,而希尔排序是将当前元素与前面增量位置上的元素进行比较,然后,再将该元素插入到合适位置。当一趟希尔排序完成后,处于增量位置上的元素是有序的。

    ②希尔排序算法的效率依赖于增量的选取

    假设增量序列为 h(1),h(2).....h(k),其中h(1)必须为1,且h(1)<h(2)<...h(k) 。

    第一趟排序时在增量为h(k)的各个元素上进行比较;

    第二趟排序在增量为h(k-1)的各个元素上进行比较;

    ..........

    最后一趟在增量h(1)上进行比较。由此可以看出,每进行一趟排序,增量是一个不断减少的过程,因此称之为缩小增量。

    当增量减少到h(1)=1时,这里完全就是插入排序了,而在此时,整个元素经过前面的 k-1 趟排序后,已经变得基本有序了,而我们知道,对于插入排序而言,当待排序的数组基本有序时,插入排序的效率是非常高的。因此,希尔排序就是利用“增量”技巧将插入排序的平均时间复杂度O(N^2)降低为亚二次方。

    二,希尔排序实例分析

    假设原始数组为: 81   94   11   96   12   35    增量序列为 h(1)=1  h(2)=3

    这里一共有两个增量序列,故一共有两趟排序。第一趟按照增量3来排序

    处于增量3上的元素集合如下:<81      96>,<94      12>,<11      35>排序之后变成:<81      96>,<12      94>,<11      35>

    即,数组变成了:81   12   11   96   94   35

    可以看出,在增量为3的各个位置上的元素都是有序的。

    经过前面一趟排序,此时数组已经基本有序,再使用增量为1的排序时(插入排序),比较的次数将会大大地降低。

    第二趟按增量为h(1)=1 来排序,排序后数组变成:

    11   12   35   81   94   96

    三,算法实现

     1 public class ShellSort {
     2     
     3     /**
     4      * 使用希尔推荐的增量: h(k)=N/2 , h(i)=h(i+1)/2
     5      * @param arr 待排序数组
     6      */
     7     public static <T extends Comparable<? super T>> void shellSort(T[] arr){
     8         
     9         int j;
    10         for(int gap = arr.length; gap >= 1; gap /=2)//排序的趟数
    11         {
    12         
    13             /*每对增量序列为:
    14              * <gap, 0> <gap+1,1> <gap+2,2>.....<arr.length-1, arr.length-1-gap>
    15              */
    16             for(int i = gap; i < arr.length; i++)//在"增量位置上" 进行插入排序
    17             {
    18                 T tmp = arr[i];
    19                 for(j = i; j >= gap && tmp.compareTo(arr[j - gap]) < 0; j-= gap)
    20                     arr[j] = arr[j - gap];
    21                 arr[j] = tmp;
    22             }
    23         }
    24     }
    25     
    26     //for test purpose
    27     public static void main(String[] args) {
    28         Integer[] arr = {81,94,11,96,12,35,17,95,28,58,41,75,15};
    29         shellSort(arr);
    30         for (Integer integer : arr) {
    31             System.out.print(integer + " ");
    32         }
    33     }
    34 }

    ①第10行for循环表示,一共有多少个增量。增量的序列的个数,就是希尔排序的趟数。

    上面的增量序列为: arr.length/2 , arr.length/2/2, arr.length/2/2/2    ....   2,  1

    ②里层的两个for循环(第16行至23行)实际上是一个插入排序

    ③第16行for循环表示:从数组的第 arr.length/2 下标起,对各组增量序列上的元素进行插入排序。gap等于arr.length/2时的排序分析如下:

    对于第一趟排序而言(gap等于arr.length/2),一共有多少组呢?答案是一共有 arr.length - arr.length/2 + 1 组。

    即:arr[arr.length/2]   arr[0] 这两个元素是一组

    arr[arr.length/2 + 1]   arr[1] 这两个元素是一组

    .....

    arr[arr.length -1]   arr[arr.length-1-gap]这两个元素是一组,此时gap等于arr.length/2

    ④第19行for循环,就是插入排序中的将当前元素与前面的元素进行比较。这里的前面元素是相差 gap 个位置上的元素。

    比如, arr[length/2]  与 arr[length/2-gap]进行比较。

    四:参考资料

    排序算法总结之快速排序

    排序算法总结之归并排序

    排序算法总结之堆排序

    排序算法总结之插入排序

    各种排序算法的总结

    至此,五大基于内存的排序算法:插入排序、希尔排序;归并排序、快速排序;堆排序 全部已经实现了一遍。

    插入排序和希尔排序是一类,本质上希尔排序就是插入排序。插入排序它的增量就是1,而希尔排序则按多个增量进行排序,增量逐渐减少至1

    归并排序和快速排序很像。之所以像,是因为它们都基于分治、递归。归并排序每次将原数组递归分成两个大小相等的子数组,而快速排序则是基于pivot元素将原数组分成两个子数组。归并排序不断地分解原数组,直到划分的两个子数组中只存在一个元素时,这时,这两个子数组可以视为都是有序的。从而,再合并两个有序的子数组,来实现排序。

    堆排序,借助了二叉堆,逻辑上是一棵完全二叉树,物理存储结构是一个一维数组。通过不断地删除堆顶元素,来实现排序。

  • 相关阅读:
    GZDBHelper C#.NET开源的数据库访问组件
    判断页面是否是静态页面
    Web应用程序dll获取当前程序集路径问题
    webAPI 上传文件
    解决.Net Core3.0 修改cshtml代码之后必须重新生成才可以看到效果
    .net core使用LESS
    test
    JS实现线程锁(Promise),NodeJS并发锁处理
    参考护照编码实现算法JS生成带字母有序编码
    centos7防火墙开放端口与关闭防火墙
  • 原文地址:https://www.cnblogs.com/hapjin/p/5538411.html
Copyright © 2020-2023  润新知