一、希尔排序
希尔排序其实就是升级版的插入排序和冒泡排序,有交换和移动两种方式,交换法甚至比插入排序还慢,所以这里讲的是希尔移动法。
public static void shell(int[] array){
for(int i=array.length/2;i>=1;i=i/2){
for(int j=0;j<i;j++){
for(int k=i+j;k<array.length;k=k+i){
int savek=k;
int save=array[k];
int l=k;
while(l-i>=0&&save<array[l-i]){
//这个判断条件是用save来进行判断的,请千万注意,并不是用array[l]<array[l-i]来判断的
array[l]=array[l-i];
savek=l-i;
l=l-i;
}
array[savek]=save;
}
}
}
}
各个参数作用:
(1)这里的i用于记录步长,从array.length的一半逐渐降到1 。
(2)j用于向下一个移位(在步长相同的情况下)。
(3)k是用来像插入排序一样往后移动。
(4)l是不断向前比较直到找到合适的位置并记录此位置。
while(l-i>=0&&save<array[l-i]){
//这个判断条件是用save来进行判断的,请千万注意,并不是用array[l]<array[l-i]来判断的
array[l]=array[l-i];
savek=l-i;
l=l-i;
}
用7,8,9,3举例,这里7,8,9假设已经被前面的递归排列好了并且此时步距为1,那么3先和9比,变成7,8,9,9,;然后和8比,变成7,8,8,9;最后变成7,7,8,9.此时记录下第一个7的位置,然后将3赋给该位置。
这种移动的思想很重要,它避免了进行一个个交换。
二、堆排序
堆排序在移动思想上类似于希尔排序。
public static void hemp(int[] array){
for(int i=array.length/2-1;i>=0;i--){
judge(array, i, array.length);
}
int length=array.length;
int save=0;
//要注意移位,并且要length--
while(length>0){
save=array[0];
array[0]=array[length-1];
array[length-1]=save;
length--;
//这里只需要从0开始就可以了,因为之前已经将树构建成大顶堆或小顶堆了。
judge(array, 0, length);
}
}
//堆排序的自下向上说的并不是一开始对于子树的排序;而是对全局,从最后一个非叶子节点开始向上排序。
//对于子树,是一定要从上到下把顺序排好的。
public static void judge(int[] array,int n,int length){
int save=array[n];
for(int i=2*n+1;i<length;i=2*i+1){//这里的i不能写i=n,然后在循环里用k=2*n+1代替
//因为后续要有i++,也就是可能会跳到右子树,所以这里要从自己的子树开始。
if(i+1<length&&array[i]>array[i+1]){
i++;
}
//注意是和save比,类似于ShellSort的移位
if(save>array[i]){
array[n]=array[i];
n=i;
}
else{
break;
}
}
array[n]=save;
}
这里分成两部分,judge和heap。
//注意是和save比,类似于ShellSort的移位
if(save>array[i]){
array[n]=array[i];
n=i;
}
上面这段代码和ShellSort的移位思想一致。