- 快速排序
快速排序总体采用了分治算法,可以分为三步:
① 找一个基准值,一般选数组最后一个元素为基准
② 遍历整个区间,每个数都和基准值作比较,并且发生一定的交换,遍历结束后使得比基准值大的(包括等于)数都在基准值的右边比基准值小的(包括等于)数都在基准值左边
③ 分治算法
分别对基准值左右两个区间的数做相同处理
直到所有小区间内没有元素了,或小区间内元素已经有序
快速排序的重点就在于第二步,我们如何能将区间分成三部分。一般我们有三种方法来分组,分别是Hover法、挖坑法、前后下标法,下面来一一介绍。
Hover法:就是分别用两个下标索引begin、end指向数组的第一个元素和最后一个元素,从begin开始与基准值比较,begin下标处的元素要是小于基准值,begin下标++,往后走,当走到的位置的数大于基准值时,begin停下来,此时end从最后面往前走,若是end处的元素大于基准值,则end- -往前走,若是走到了所在元素大于基准值时,end停下来。此时begin和end都遇到了瓶颈走不动了,我们只需要交换begin和end处的元素即可继续进行,直到begin和end相遇为止,此时即是基准值应该处的位置,我们交换begin(或end)下标处的元素和基准值即可。
挖坑法:挖坑法首先将区间最后边的基准值保存起来,此处就像形成了一个坑,和Hover法一样,也是用两个下标索引begin和end,分别放在数组最左边和最右边。从begin开始和基准值比较,如果小于基准值,begin++往前走,若是大于基准值,则将该值放入坑中,此时begin处就变成了一个坑,再从end开始,end往前走,若是大于基准值则end- -往前走,若是小于基准值,则将end处的值填在begin处的坑中,重复上述步骤,直到begin和end相遇,相遇处即是基准值所应该在的地方。将基准值填在这里即可。
前后坐标法:前后坐标法是用了两个下标索引div和i,这两个索引都在left处,将i处的值与基准值进行比较,当该值小于等于基准值时,交换div和i处的值,div和i同时++向后走,如果坐标为i处的值大于基准值时,只让i++向后走,当i遍历完时,div所在的位置就是基准值的位置。
此处只是对将数组分成三部分做了文字描述,推荐这篇文章,作者利用动画将快速排序讲的更为细致。快速排序优秀文章
快速排序代码如下:
public class Test {
// 交换两个数
private static void swap(int[] array, int x, int y){
int temp = array[x];
array[x] = array[y];
array[y] = temp;
}
// 快速排序--升序
// Hover法
private static int partion(int[] array, int left, int right){
int begin = left;
int end = right;
while(begin < end){
while(begin < end && array[begin] <= array[right]){
begin++;
}
while(begin< end && array[end] >= array[right]){
end--;
}
swap(array,begin,end);
}
swap(array,begin,right);
return begin;
}
// 挖坑法
private static int partion1(int[] array,int left,int right){
int begin = left;
int end = right;
int pivort = array[right];
while(begin < end){
while(begin < end && array[begin] <= pivort){
begin++;
}
array[end] = array[begin];
while(begin < end && array[right] >= pivort){
end--;
}
array[begin] = array[end];
}
array[begin] = pivort;
return begin;
}
// 前后下标法
private static int partion2(int[] array,int left,int right){
int div = left;
int i = left;
while(i < right){
if(array[i] > array[right]){
swap(array,div,i);
div++;
i++;
}else{
i++;
}
}
swap(array,div,right);
return div;
}
public static void quickSort(int[] array,int left,int right){
// size == 1
if(left == right){
return;
}
// size == 0
if(left < right){
return;
}
int stand = partion(array,left,right);
quickSort(array,0,stand-1);
quickSort(array,stand+1,right);
}
private static void quick(int[] array) {
if(array.length>0){
quickSort(array,0,array.length-1);
}
}
public static void main(String[] args) {
int[] array = {9,5,-1,10,0,2,7,3,8,6};
// selectSort(array);
quick(array);
for(int i = 0; i < array.length; i++){
System.out.print(array[i]+"、");
}
}
}
- 堆排序:由于选择排序在遍历过程中,会进行多次重复的比较,因此我们可以采用堆排序来解决这种问题。首先我们把数组建成一个大堆,再对这个大堆做向下调整,向下调整的前提是该堆中只有一个位置不满足堆的性质,其余位置都满足堆的性质了。向下调整分为三步:
① 我们首先需要判断传进来的下标是不是叶子节点
② 找出该节点的孩子中较大的那一个孩子
③ 比较较大的孩子和父节点的大小,若是孩子节点大,则交换,然后继续做向下调整
代码如下:
public static void heapSort(int[] array) {
for (int i = 0; i < array.length; i++) {
createMaxHeap(array, array.length - 1 - i);
swap(array, 0, array.length-1-i);
}
}
public static void createMaxHeap(int[] data, int lastIndex) {
// 从lastIndex处节点(最后一个节点)的父节点开始
for (int i = (lastIndex-1)/2; i >= 0; i--) {
int k = i;
while (2 * k + 1 <= lastIndex) {
int biggerIndex = 2 * k + 1;
if (biggerIndex + 1 <= lastIndex) {
if (data[biggerIndex] < data[biggerIndex + 1]) {
biggerIndex++;
}
}
if (data[k] < data[biggerIndex]) {
swap(data, k, biggerIndex);
k = biggerIndex;
} else {
break;
}
}
}
}