zoukankan      html  css  js  c++  java
  • 数组排序-希尔排序(Shell's Sort)


    介绍:

        希尔排序是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因Donald Shell于1959年提出而得名。


    历史:

        希尔排序按其设计者希尔(Donald Shell)的名字命名,该算法由1959年公布。一些老版本教科书和参考手册把该算法命名为Shell-Metzner,即包含Marlene Metzner Norton 的名字,但是根据Metzner本人的说法:“我没有为这种算法做任何事,我的名字不应该出现在算法的名字中”。
        希尔排序是基于插入排序的以下两点性值而提出改进方法的:
          1.插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
          2.但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。


    基本思想:

        取一个小于n的整数d1作为一个增量,把文件的全部记录数组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量,即所有记录放在同一组中进行直接插入排序为止。
        该方法实质上是一种分组插入方法。
        比较相隔较远距离(增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。Donald Shell于1959年在以他的名字命名的派苏算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d,对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
        一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
        给定实例的shell排序的排序过程。
        假设待排序的文件有10个记录,其关键字分别是:
         86,11,54,34,53,12,45,81,19,65
        增量序列的取值依次为:
         5,2,1


    稳定性:

        由于多次插入排序,我们知道一次插入排序是不稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。


    排序过程:

        缩小增量
        希尔排序属于插入类排序,是将整个有序序列分割成若干小的子序列分别进行插入排序。
        排序过程:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止。


    示例:

    package com.lxj.cnblogs;
    
    /**
     * @author 刘小将
     * 测试希尔排序
     */
    public class ShellSort {
    
        public static void main(String[] args) {
            int[] a = {86,11,54,34,53,12,45,81,19,65};  //声明一个数组并给定值
            printArray(a);                              //输出原始数组到控制台
            int k = 3;
            while (k >= 1){
                shellSort(a,k);
                printArray(a);
                k--;
            }
        }
    
        /**
         * 希尔排序算法
         * @param a
         * @param k
         */
        public static void shellSort(int[] a,int k){
            int i = 0;
            int j = 0;
            for(i = k;i < a.length;i+=k){
                int temp = a[i];
                if(a[i-k]>a[i]){
                    for (j = i-k;j >= 0; j-=k){
                        if(temp<a[j]){
                            a[j+k] = a[j];
                        }else{
                            break;
                        }
                    }
    
                    a[j+k] = temp;
    
                }
            }
        }
    
        /**
         * 输出方法
         * @param array
         */
        public static void printArray(int[] array){
            for(int i : array){
                System.out.print(i+" ");
            }
            System.out.println();
        }
    
    }
    

    运行结果:

    86 11 54 34 53 12 45 81 19 65 
    34 11 54 45 53 12 65 81 19 86 
    19 11 34 45 53 12 54 81 65 86 
    11 12 19 34 45 53 54 65 81 86 
    

    原理:


    算法分析:

      优劣:
        不需要大量的辅助空间,和归并排序一样容易实现。希尔排序是基于插入排序的一种算法,在此基础之上增加了一个新的特性,提高了效率。希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n2),而Hibbard增量的希尔排序的时间复杂度为O(n(3/2)),希尔排序时间复杂度的下界是n*log2n。希尔排序没有快速排序算法快O(n(logn)),因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择。但是比O(n^2)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡,几乎任何排序工作在开始时都是都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法。本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少其复制的次数,速度要快很多,原因是:当n值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。当n值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。正是这两种情况的结合才使希尔排序效率比插入排序高很多。Shell算法的性能与所选取的分组长度序列有很大的关系。只对特定的待排序记录序列,可以准确地估算关键词的比较次数和对象移动次数。想要弄清关键词比较次数和记录移动次数与增量选择之间的关系,并给出完整的数学解析,至今仍是数学难题。

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

        因此,希尔排序在效率上较直接插入排序有较大改进。

  • 相关阅读:
    JQuery
    JS
    PHP
    Java并发编程:CopyOnWrite容器的实现
    Java并发编程:并发容器ConcurrentHashMap
    Java并发编程:同步容器
    Java并发编程:ThreadLocal
    Java并发编程:sleep、wait、yield对比
    Java并发编程:volatile关键字解析
    Java并发编程:Lock
  • 原文地址:https://www.cnblogs.com/joyfulcode/p/13753710.html
Copyright © 2011-2022 走看看