数据结构和算法12 之希尔排序

 上一章我们学习了冒泡排序、选择排序和插入排序三种基础排序算法,这三种排序算法比较简单,时间复杂度均为O(N2),效率不高。这节我们讨论一个高级排序算法:希尔排序。希尔排序是基于插入排序的,插入排序有个弊端,假设一个很小的数据项在很靠近右端的位置上,那么所有的中间数据项都必须向右移动一位,这个步骤对每一个数据项都执行了将近N次的复制,这也是插入排序效率为O(N2)的原因。

        希尔排序的中心思想是将数据进行分组,然后对每一组数据进行插入排序,在每一组数据都有序后,再对所有的分组利用插入排序进行最后一次排序。这样可以显著减少数据交换的次数,以达到加快排序速度的目的。

这种思想需要依赖一个增量序列,我们称为n-增量,n表示进行排序时数据项之间的间隔,习惯上用h表示。为了很好的理解增量排序,我们看下面的示意图:

        上图显示了增量为4时对10个数据项进行排序的示意图,一趟后0、4、8三个位置上的数据项已经排好序,接下来,算法向右移动一步,对1、5、9三个位置的数据项进行排序,这个过程一直持续进行,直到所有数据项都完成了一次4-增量排序,然后缩小增量,再重复上面的过程,最后一次增量为1,对所有的数据项进行一次插入排序,从而完成了希尔排序的全部步骤。因为最后一趟都已经基本有序了,所以复杂度没有向普通插入排序那么大了。

       说到这里,大家应该可以明白,增量序列在希尔排序中是很重要的。一般好的增量序列都有2个共同的特征:

        1. 最后一个增量必须为1,保证最后一趟是一次普通的插入排序;

        2. 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况

       这篇文章中我们使用增量序列用 h = 3 * h + 1来生成。h初值被赋予1,然后使用该公式生成序列1、4、13、40、121、364等等,当间隔大于数组大小的时候停止,使用序列的最大数组作为间隔开始希尔排序过程,然后没完成一次排序,用倒推公式 h = (h - 1) / 3来减小间隔,保证最后一次h=1,完成最后一次插入排序。

        说完了原理,下面看看具体实现代码:

[java] view plain copy

 

  1. public void shellSort2() {  
  2.     int h = 1;  
  3.     while(h <= nElem / 3) {  
  4.         h = h * 3 + 1; //增量间隔  
  5.     }  
  6.       
  7.     while(h > 0) {  
  8.         for(int i = h; i < nElem; i++) {  
  9.             //每个增量间隔内,实现插入排序,两个for循环和插入排序一样,只不过第二个for循环里参数略有调整而已,和h有关  
  10.             for(int j = i; j < nElem; j += h) {  
  11.                 for(int k = j; (k - h >= 0) && a[k] < a[k - h]; k -= h) {  
  12.                     swap(k, k-h);  
  13.                 }  
  14.             }  
  15.         }  
  16.         h = (h-1) / 3;  
  17.     }  
  18. }  

        算法分析:希尔排序时间复杂度平均为O(NlogN),最好与最坏情况要根据具体的增量序列来判断,对于不同的增量序列有不同的复杂度。希尔排序的性能优于直接插入排序,因为在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来随着增量逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但是由于已经局部排过序了,所以已经接近有序状态,新的一趟排序过程也较快。因此,希尔排序在效率上较直接插入排序有较大的改进。

        希尔排序是不稳定的,因为不同的间隔对应的数据是独自比较的,如果a=b,但是不在同一个间隔上,显然会出现前后颠倒的情况,所以希尔排序是不稳定的。

        空间复杂度为O(1),不需要额外的存储空间。

        希尔排序就介绍到这,如有错误之处,欢迎留言指正~+

转载:http://blog.csdn.net/eson_15/article/details/51168467

时间: 2024-11-05 00:53:52

数据结构和算法12 之希尔排序的相关文章

Java排序算法总结之希尔排序_java

本文实例讲述了Java排序算法总结之希尔排序.分享给大家供大家参考.具体分析如下: 前言:希尔排序(Shell Sort)是插入排序的一种.是针对直接插入排序算法的改进.该方法又称缩小增量排序,因DL.Shell于1959年提出而得名.本文主要介绍希尔排序用Java是怎样实现的. 希尔排序(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序.希尔排序并不稳定,O(1)的额外空间,时间复杂度为O(N*(logN)^2).最坏的情况下的执行效率和在平均情况下的执行效率相

数据结构和算法17 之拓扑排序

  这一节我们学习一个新的排序算法,准确的来说,应该叫"有向图的拓扑排序".所谓有向图,就是A->B,但是B不能到A.与无向图的区别是,它的边在邻接矩阵里只有一项(友情提示:如果对图这种数据结构部不太了解的话,可以先看一下这篇博文:数据结构和算法之 无向图.因为拓扑排序是基于图这种数据结构的). 有向图的邻接矩阵如下表所示:   A B C A 0 1 1 B 0 0 1 C 0 0 0         所以针对前面讨论的无向图,邻接矩阵的上下三角是对称的,有一半信息是冗余的.而

Java排序算法&amp;amp;nbsp;希尔排序

希尔排序(Shell Sort)是插入排序的一种.是针对直接插入排序算法的改进.该方法又称缩小增量排序,因DL.Shell于1959年提出而得名.本文主要介绍希尔排序用Java是怎样实现的. AD: 希尔排序(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序.希尔排序并不稳定,O(1)的额外空间,时间复杂度为O(N*(logN)^2).最坏的情况下的执行效率和在平均情况下的执行效率相比相差不多. 基本思想: 先取一个小于n的整数d1作为第一个增量,把文件的全部记录

排序算法系列之希尔排序

算法排序之希尔排序 希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本.  基本思想    先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组内进行直接插人排序:然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<-<d2<d1),即所有记录放在同一组中进行直接插入排序为止. 希尔排序的时间性能优于直接插入排序的原因: ①当文件初态基本有序时直接插入排

常用内部排序算法之五:希尔排序

前言 在前面介绍常用内部排序算法的文章中,我们知道在简单排序算法中,直接插入排序算法的时间复杂度是要优于冒泡排序和简单选择排序的.而这里要讲的希尔排序则是优于直接插入排序的.而且希尔排序打破了原来简单排序中时间复杂度不能超过O(n2)的神话.这也是希尔排序伟大的地方,而且希尔排序不同于之前三种简单排序,希尔排序是以一个科学家的名字进行命名的. 希尔排序的原理 由于希尔排序是在直接插入排序的基础上进行改进的,所以为了更好理解希尔排序,需要再次将直接插入排序请出来.我们知道直接插入排序的基本思想是将

数据结构和算法15 之二叉树排序

 顾名思义,二叉树排序就是利用二叉搜索树的特点进行排序,前面提到过二叉搜索树的特点是,左子节点比自己小,右子节点比自己大,那么二叉树排序的思想就是先将待排序序列逐个添加到二叉搜索树中去,再通过中序遍历二叉搜索树就可以将数据从小到大取出来.如果对二叉树还不太了解,请看这篇博文:数据结构和算法之二叉树 ,这里不再赘述. 下面我们来看看二叉树排序的实现: [java] view plain copy   public class Tree2Sort {       private Node root;

数据结构和算法11 之基础排序

前10节我们学习了一些经典的数据结构,从这节开始,我们将学习一些排序算法.这一节我们先学习几个基础排序算法:冒泡排序,选择排序和插入排序. 1. 冒泡排序         冒泡排序算法运行起来非常慢,但在概念上它是排序算法中最简单的,因此冒泡排序算法在刚开始研究排序技术时是一个非常好的算法.冒泡排序算法的基本流程是:每一轮从头开始两两比较,将较大的项放在较小项的右边,这样每轮下来保证该轮最大的数在最右边. 算法程序如下: [java] view plain copy   public void 

数据结构与算法C#版笔记--排序(Sort)-下

5.堆排序(HeapSort) 在接触"堆排序"前,先回顾一下数据结构C#版笔记--树与二叉树 ,其中提到了"完全二叉树"有一些重要的数学特性: 上图就是一颗完全二叉树,如果每个节点按从上到下,从左至右标上序号,则可以用数组来实现顺性存储,同时其序号: 1.如果i>1,则序号为i的父结节序号为i/2(这里/指整除) 言外之意:整个数组前一半都是父节点,后一半则是叶节点 2.如果2*i<=n(这里n为整颗树的节点总数),则序号为i的左子节点序号为2*i 3

Java数据结构及算法实例:选择排序 Selection Sort_java

/** * 选择排序的思想: * 每次从待排序列中找到最小的元素, * 然后将其放到待排的序列的最左边,直到所有元素有序 * * 选择排序改进了冒泡排序,将交换次数从O(N^2)减少到O(N) * 不过比较次数还是O(N) */ package al; public class SelectSort { public static void main(String[] args) { SelectSort selectSort = new SelectSort(); int[] elements