首页 > 代码库 > 排序算法总结之希尔排序
排序算法总结之希尔排序
希尔排序(Shell Sort)是插入排序的一种,其实质就是分组插入排序,该方法又称缩小增量排序,因D.L.Shell于1959年提出而得名。它是对直接插入排序的一种改进,通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使得数据项大跨度的移动。
基本思想
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
假设当前增量h为4(间隔),对[9,-16,21,23,-30,-49,21,30,30]进行shell排序:
①[9,-16,21,23,-30,-49,21,30,30]—>[-30,-16,21,23,9,-49,21,30,30]
②[-30,-16,21,23,9,-49,21,30,30]—>[-30,-49,21,23,9,-16,21,30,30]
③[-30,-49,21,23,9,-16,21,30,30]—>[-30,-49,21,23,9,-16,21,30,30]
④[-30,-49,21,23,9,-16,21,30,30]—>[-30,-49,21,23,9,-16,21,30,30]
第一趟只需保证0,4,8上的数据有序,完成后整体右移一步,对1,5上的数据进行排序。。。这样的过程持续进行,直到所有的数据项都完成了以4为增量的排序。
接下来减少增量,直到完成以1为增量的排序。
从上面的分析可以看出,shell排序的关键在于增量序列的值,常用的增量序列由Knuth提出,该序列从1开始,由如下公式产生:h = h*3 + 1
程序中要反向计算增量序列:h = (h-1)/3
Java实现代码
package com.liuhao.sort; import java.util.Arrays; public class ShellSort { public static void shellSort(DataWrap[] data){ int arrayLength = data.length; int h = 1;//保存可变增量 //按h * 3 + 1得到增量序列的最大值 while(h <= arrayLength/3){ h = h * 3 + 1; } while(h > 0){ System.out.println("====h的值:" + h + "===="); for(int i = h; i < arrayLength; i++){ DataWrap tmp = data[i]; if(data[i].compareTo(data[i - h]) < 0){ int j = i - h; //整体后移h位 for(; j >= 0 && data[j].compareTo(tmp) > 0; j-=h){ data[j+h] = data[j]; } data[j+h] = tmp; } System.out.println(Arrays.toString(data)); } h = (h-1) / 3; } } public static void main(String[] args) { DataWrap[] data = http://www.mamicode.com/{>排序效果:
算法分析
1.增量序列的选择
Shell排序的执行时间依赖于增量序列。
好的增量序列的共同特征:
① 最后一个增量必须为1;
② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在nl.25到1.6n1.25之间。
2.Shell排序的时间性能优于直接插入排序
希尔排序的时间性能优于直接插入排序的原因:
①当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
②当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。
③在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插人排序有较大的改进。
3.稳定性
希尔排序是不稳定的。参见上述实例,该例中两个相同关键字49在排序前后的相对次序发生了变化。排序算法总结之希尔排序