希尔排序是基于直接插入排序的,它在直接插入排序中增加了一个新特性,大大的提高了插入排序的执行效率
希尔排序通过加大插入排序中元素的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能够大跨度的移动。当这些数据项排过一趟序后,希尔排序算法减小数据项的间隔再进行排序,依次进行下去,最后间隔为1时,就是我们上面说的简单的直接插入排序。
③、排序间隔选取
对于10个元素,我们选取4的间隔,那么100个数据,1000个数据,甚至更多的数据,我们应该怎么选取间隔呢?
希尔的原稿中,他建议间隔选为N/2,也就是每一趟都将排序分为两半,因此对于N=100的数组,逐渐减小的间隔序列为:50,25,12,6,3,1。这个方法的好处是不需要在开始排序前为找到初始序列的间隔而计算序列,只需要用2整除N。但是这已经被证明并不是最好的序列。
间隔序列中的数字互质是很重要的指标,也就是说,除了1,他们没有公约数。这个约束条件使得每一趟排序更有可能保持前一趟排序已经排好的结果,而希尔最初以N/2的间隔的低效性就是没有遵守这个准则。
所以一种希尔的变形方法是用2.2来整除每一个间隔,对于n=100的数组,会产生序列45,20,9,4,1。这比用2会整除会显著的改善排序效果。
还有一种很常用的间隔序列:在不小于len/3下间隔序列step*3 + 1,每次循环后间隔(step-1)/3
总之,无论是什么间隔序列,最后必须满足一个条件,就是逐渐减小的间隔最后一定要等于1,因此最后一趟排序一定是简单的插入排序。
代码
/**
*
*/
package cn.bzu.datastructure.practice;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
/**
*
* @projectName datastructure
*
* @ClassName ShellSort
* @exception 希尔排序的算法
*/
public class ShellSort {
@Test
public void test() {
int date[] = { 675, 754, 24,12,8,5,2 };
shellSort2(date);
}
//间隔是数组的二分之一,一次减少
public static void shellSort(int date[]) {
System.out.println("原素组是" + Arrays.toString(date));
int len = date.length;// 数组的长度
int temp;// 保存临时值得变量
int interval; // 元素之间的间隔
for (interval = len / 2; interval > 0; interval /= 2) {// interval一半一半的缩小
int count = 1;
for (int i = interval; i < len; i++) {
int j = i;
temp = date[j];
while (j - interval >= 0 && temp < date[j - interval]) {
date[j] = date[j - interval];// 改变值
j = j - interval;// 改变j的值
}
date[j] = temp; // 将最小的值date[j]
System.out.println("第" + count + "次排序" + Arrays.toString(date));
count++;
}
}
}
//间隔是len/3+1,依次减少
public static void shellSort2(int date[]) {
System.out.println("原数组是:"+Arrays.toString(date));
int len = date.length;
int temp;
int interval = 1;//间隔长度
while(interval<=len/3) {//间距取长度的三分之一
interval = interval*3+1;//找到最大的间隔数
}
while(interval>0) {//分别对每个增量间隔进行排序
for(int i =interval;i<len;i++) {
temp = date[i];
int j = i;
while((j>interval-1)&&(temp<=date[j-interval])) {
date[j]= date[j-interval];
j = j-interval;
}
date[j] = temp;//将temp中的值放到比较的最大值得地方。
}
interval = (interval-1)/3;//间隔递减
System.out.println("排序的数组"+Arrays.toString(date));
}
}
}
结果输出;
原数组是:[675, 754, 24, 12, 8, 5, 2]
排序的数组[8, 5, 2, 12, 675, 754, 24]
排序的数组[2, 5, 8, 12, 24, 675, 754]