《算法(第四版)》排序-----希尔排序


由“插入排序”可知,它是一种不稳定的算法,当序列已经接近有序时,排序速度较快,但是如果最小的在最后面,那可能需要最小的交换了n-1次才能换到最前面。

希尔排序在插入排序的基础上进行了改进,交换不相邻的元素,以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序,具体做法如下。

1.假设数组长度为N,先以较大的增量gap将N进行划分几组,例如数组为{2,4,8,1,9,10,5,6,7,3}(N=10),增量gap=7,此时分组情况为{2,4,8,1,9,10,5},{6,7,3}

2.然后将每组的对应位置的数进行比较,小的交换到前面。2比6小不动,4比7小不动,3比8小,换前面,这一轮回下来的结果{2,4,3,1,9,10,5,6,7,8}

3.再将gap减小,重新分组,例如gap=4,分组情况为{2,4,3,1},{9,10,5,6},{7,8}

4.再将对应位置比较,交换,2<7<9,所以各自交换为{2,4,3,1,7,10,5,6,9,8},4<8<10,所以交换后的结果{2,4,3,1,7,8,5,6,9,10}

5.在减小gap,直到增量为1时,还是按照插入排序的算法,将顺序排完。{1,2,3,4,5,6,7,8,9,10}


具体算法如下

public static <T> void shellSort(Comparable<T>[] a){// 将a[] 升序排列
    int N = a.length;
    int h = 1;
    while(h < N/3)
        h = 3 * h + 1;//1,4,13,40,121,...h为每次第二组的第一个元素的坐标,这样先两i=h后i++也就是第二组的后面的一直在和第一组的进行比较
    while(h >= 1){//将数组变为h有序
        for(int i = h; i < N; i++){//将a[i]插入到a[i-h],a[i-2*h],a[i-3*h]...中
            //j>=h目的是a[j-h]有意义,j -= h,是为了让a[i]和a[i -h],a[i -2*h],...比较
            for(int j = i; j >= h&& less(a[j], a[j-h]); j -= h){
                exch(a,j,j-h);
            }
        }
        //逐步减小h,直至h=1时结束
        h = h / 3;
    }
}


测试程序如下:

public class ShellSort {
	public static <T> void shellSort(Comparable<T>[] a){
		// 将a[] 升序排列
		int N = a.length;
		int h = 1;
		while(h < N/3)
			h = 3 * h + 1;//1,4,13,40,121,...
		while(h >= 1){//将数组变为h有序
			for(int i = h; i < N; i++){//将a[i]插入到a[i-h],a[i-2*h],a[i-3*h]...中
				for(int j = i; j >= h&& less(a[j], a[j-h]); j -= h){
					exch(a,j,j-h);
				}
			}
			h = h / 3;
		}
	}
	private static <T>boolean less(Comparable<T> v, Comparable<T> w){
		return v.compareTo((T) w) < 0;
	}
	private static <T> void exch(Comparable<T>[] a, int i, int j){
		Comparable<T> t = a[i];
		a[i] = a[j];
		a[j] = t;
	}
	private static <T> void show(Comparable<T>[] a){
		//在单行中打印数组
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
	}
	public static <T> boolean isSorted(Comparable<T>[] a){
		//测试数组元素是否有序
		for (int i = 0; i < a.length; i++) {
			if(less(a[i], a[i-1]))
				return false;
		}
		return true;
	}
	public static void main(String[] args) throws FileNotFoundException {
		// TODO Auto-generated method stub
//		String[] a = {"S","O","R","T","E","X","A","M","P","L","E"};
		Integer[] a = {-5,8,9,5,7,1,-68,1,5,33,100,563,526,16,21,68,-88};
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println();
		shellSort(a);
		assert isSorted(a);
		show(a);
		
	}
}

希尔排序需要比较的次数为  N^(4/3),或N^(5/4)...,它的性能提高了20%~40%


书上说:“有经验的程序员有时会选择希尔排序,因为对于中等大小的数组它的运行时间是可以接受的,它的代码量很小,且不需要使用额外的内存空间,如果需要解决一个排序问题而又没有系统排序函数可用,可以先用希尔排序,然后再考虑是否值得将它替换为更加复杂的排序算法。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值