排序算法总结之希尔排序

希尔排序(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 = {
				new DataWrap(9, "")
				,new DataWrap(-16, "")
				,new DataWrap(21, "")
				,new DataWrap(23, "*")
				,new DataWrap(-30, "")
				,new DataWrap(-49, "")
				,new DataWrap(21, "")
				,new DataWrap(30, "")
				,new DataWrap(3, "")
				,new DataWrap(67, "")
				,new DataWrap(35, "")
				,new DataWrap(5, "")
		};
		
		System.out.println("排序之前:" + Arrays.toString(data));
		shellSort(data);
		System.out.println("排序之后:" + Arrays.toString(data));
	}
	
}

排序效果:

排序算法总结之希尔排序_第1张图片


算法分析

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

你可能感兴趣的:(Java进阶,数据结构与算法分析,笔试面试,希尔排序,java,排序算法)