高级排序-------希尔排序
原理:将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序......最后选择增量为1,即使用直接插入排序,使最终数组成为有序。
增量:希尔排序通过加大排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度地移动。当这些数据项排过一趟序后,希尔排序算法减小数据项的间隔再进行排序,依次进行下去。进行这些排序时数据项之间的间隔被称为增量。
比如:采用3*h+1增量,当有十个数时,最大增量跨度为4
4为增量排序0,4,8号数据项
当对0、4、8号数据项完成排序之后,算法向右移一步,对1、5、9号数据项进行排序。这个排序过程持续进行,直到所有的数据项都已经完成了4增量排序,也就是说所有的间隔为4的数据项之间都已经排列有序。
在完成以4为增量的希尔排序之后,数组可以看成是由4个子数组组成(0,4,8),(,1,5,9),(2,6,)和(3,7),这四个子数组内分别是完全有序的。
减小间隔:当进行过第一趟排序后,在制定间隔位置上的数已经有序了,这时应减小间隔继续排序,直到间隔变成1
根据上诉增量公式,再每次完成一次排序后,用公式h=(h-1)/3来减小间隔
实例:
先初始化跨度h = 1,通过while 循环来获取最大跨度
int h = 1;//初始化跨度
//设置跨度,将数组分成若干组
while (h <= arr.length/3){
h = 3*h +1;
}
while循环中的判断来控制循环的结束,当跨度小于1的时候表示排序完成,循环结束
while (h>0){ for (outer = h;outer < arr.length;outer ++){//通过循环比较,将对应位置的数据进行大小交换 temp = arr[outer]; inner = outer; System.out.println("temp = "+temp+";outer = "+outer); while (inner > h-1 && arr[inner - h] >= temp){ arr[inner] = arr[inner - h]; inner -=h; } arr[inner] = temp; } //上述for循环跳出来之后,整个数组中,被分成的若干小数组已经有序了 h = (h - 1)/3;//调整数组间隔,直到数组间隔小于1时,排序完成跳出while循环 }
高级排序------快速排序
快速排序分三个基本步骤
1、 把数组或者子数组划分成左边(较小的关键字)的一组和右边(较大的关键字)的一组
2、 调用自身对左边的一组进行排序
3、 再次调用自身对右边的一组进行排序
经过一次划分之后,所有在左边子数组的数据项都小于在右边子数组的数据项,只要对左边子数组和右边子数组分别进行排序,整个数据就是有序的了,如何对子数组进行排序,通过递归的调用排序算法自身就可以。
递归调用排序子数组
选择枢纽(三数据项取中)
partitionIn()方法获取枢纽
选择具体的一个数据项的关键字的值作为枢纽:称这个数据项为pivot(枢纽)
可以选择任意一个数据项作为枢纽,一般可以选择带划分的子数组最右端的数据作为枢纽,但是会有一种情况(已经有序,或者最右端的数值最大),这样等于是进行一次排序后,数组还是一个,与其他排序相比体现不出来,因此,我们一般取数组或者子数组的三个值进行比较,选取中间值作为枢纽。
private int medianOf3(int left, int right) { int center = (left + right)/2; if (arr[center] > arr[right]){ swap(center,right); } if (arr[left] > arr[right]){ swap(left,right); } if (arr[left] > arr[center]){ swap(left,center); } swap(center,right - 1); return arr[right-1]; }
交换枢纽位置
获取第一次划分后,关键字应停放的位置
private int partitionIn(int left, int right, int pivot) { int leftPtr = left; int rightptr = right - 1; while (true){ while (arr[++leftPtr] < pivot); while (rightptr > 0 && arr[--rightptr] > pivot); if (leftPtr >= rightptr){ break; }else { swap(leftPtr,rightptr); } } swap(leftPtr,right - 1); return leftPtr; }
由于中值划分要求至少含有四个数据项,所以不能应用于只有两个或者三个数据项的子数组,所以,当子数组中只有一个数据项时方法立即返回,有两个数据项时,如果需要则交换这两个数据项,有三个数据项时对三个数据项进行排序。
private void manulSort(int left, int right) { int size = right - left +1; if (size <= 1){ return; } if (size == 2){ if (arr[left] > arr[right]){ swap(left,right); return; } }else { if (arr[left] > arr[right - 1]){ swap(left,right-1); } if (arr[left] > arr[right]){ swap(left,right); } if (arr[right - 1] > arr[right]){ swap(right-1,right); } } }
快速排序总的调用方式
private void recQuickSort(int left,int right){ int size = right - left + 1; if (size <= 3){ manulSort(left,right); }else { int pivot = medianOf3(left,right); int partition = partitionIn(left,right,pivot); //递归排序的时候,由于中间位置partition已经有序,所以从它的左边一位,或者右边一位开始 recQuickSort(left,partition-1); recQuickSort(partition+1,right); } }