四、希尔排序

希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的
1.插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
2.但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

思路
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
  
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

假设数据:a {25,84 ,35,75,5,16,78,5}
上述数据如果使用插入排序的话最后一次将移动数据5至首位,此时数据量小还好,当数据量达到很大规模时而小的数据大都在后面则会消耗大量时间在移动位置上。希尔排序就是保证这些小的靠后的数据向前移动一大步
设置增量区间值:h =( h*2+1<=a.length) 可以看出h的区间值为 1,3,7
数据解析:
h:区间
i:比较数据 i=h,i会随着数据递增
j:初始化待比较数据 j=i-h
假设此时h=7
数据区间为:{25,84 ,35,75,5,16,78},{5}
i=7,h=7,j=0
1.比较座标0-7,交换数据{5,84 ,35,75,5,16,78,25}
2.j<a.length 结束这波比较。
可以看出5直接与首位数据25做比较,其直接交换位置,避免一步步前移动了

第二轮此时h=3
数据:{5,84 ,35,75,5,16,78,25}
数据区间为:{5,75 ,78},{84,35,5,16,25}
i=h=3,j=i-h,随着i的递增j会发生变化,主要是比较j与i的值,相隔区间h

1.比较 0-3, 也就是 5与75的值,小于则保持不变,按照区间取下一个值时发现j-h=-3超出座标范围则结束,递增i的值

2.随着i的递增, 主要比较其:
3-0,
4-1,
5-2,
6-3,6-0
7-4,7-1
由上面数据交换比较,可以看出其是按照数据的区间来进行的5-2=3,6-3=3,由于6包含两个3区间,所以6-3-3=0, 7也是一样的道理
第三轮此时h=1
由于进行了多次数据交换,就不显示此时数据状态了,主要看起数据区间
当i=1;比较其1-0
当i=2;比较其2-1,2-0
当i=3;比较其3-2,3-1, 3-0



当i=7;比较其 7-6,7-5,7-4 ,7-3,7-2,7-1,7-0
由此可以看出其就是一个普通的插入排序,由于前几轮的区间比较已经将数据接近排序状态,所以最后一轮的比较其实只会交换少量的数据来完成最终的排序。

代码

	//希尔排序--加强版的插入排序
	public static void shellSort(int[] arr){
		if(arr == null || arr.length == 0)
			return;
		int h = 0;
		int len = arr.length;
		while(h<=len){
			h = h*2+1;	//增量区间
		}
		
		while(h>=1){//初始区间大于等于1
			//根据h把数据分为两个部分
			for (int i = h; i < len; i++) {
				int j = i-h;//起始比较值座标
				int get = arr[i]; //初始比较值
				while(j>=0 && arr[j] > get){ //比较某个区间两端的值,随着区间越来越小,数据逐渐接近排序后的结果
					arr[j+h] = arr[j];
					j = j-h;	     //减去区间值,比较相隔多个区间范围两端的值
				}
				arr[j+h]=get;
			}
			h = (h-1)/2;//递减区间,随着区间的递减,当区间值为1时也就是相当于普通插入排序操作。但是这时数据已经接近于排序后的结果,不会出现较大距离的移动。从而提高数据效率
		}
	}

按照区间进行数据排序,使其数据接近排序后的结果,最后执行普通插入排序实现数据排序,主要在于用比较交换数据位置的时间在优化插入排序时的大量移动数据操作

来源于www.cnblogs.com/eniac12/p/5329396.html#s32

点赞