题目:对于一个int数组,请编写一个快速排序算法,对数组元素排序。给定一个int数组A及数组的大小n,请返回排序后的数组。测试样例:[1,2,3,5,2,3],6 [1,2,2,3,3,5]
思路:
快速排序是使用二分思想,通过递归来实现排序的。对于一个数组,它先随机选择一个元素A将数组分成两个部分,将小于元素A的元素放到数组的A的左边,将大于A的元素放到A的右边,然后再对左右两侧的子数组分别进行递归的分割排序,递归的边界条件是当最终分割得到的数组只有一个元素,即被元素A分割得到的某个数组大小是1,那么这个大小为1的数组就直接就是结果,不用再进行递归了,对于元素数目多于1的数组继续进行分割递归排序。快速排序的关键①随机元素的选取,直接决定了排序的复杂度,但是通常选取数组的中间元素作为分界元素;②已知数组array和其中的分界元素array[k],如何将数组中小于等于array[k]的元素放在其左边而将大于array[k]的元素放在其右边?这里采取的策略是,创建一个抽象的小于等于区间{}。先将array[k]与数组的最后一个元素array[length-1]进行交换,使得分界元素位于最后面,然后假设初始的小于等于array[k]的数组smallArray[]在array[0]位置的左侧,初始为空,即为{};然后使用两个指针p1,p2对数组array[]和smallArray[]进行遍历,p1初始值是0,p2初始值是-1,将array[p1]与array[k]=A进行比较,如果array[p1]>A,那么p2保持不变,p1++,表示这个元素大于A,不能放入到左侧的{}中;如果array[p1]<A,那么说明A元素小于分界值A,可以放入到左侧的{}中,此时将p2++,表示{}要开始容纳一个元素,然后将array[p2]与array[p1]进行交换,此时得到{0}5641723;然后再p1++即开始遍历下一个元素,即当遇到一个元素array[p1]<A时总是先对p2进行p2++,再将其array[p2]与array[p1]进行交换,再对p1进行p1++。即快速排序也是基于交换的,需要进行nlogn次交换。
快速排序的时间复杂度O(nlogn),空间复杂度O(1),不稳定。
采用该思路失败的代码:
import java.util.*;
//快速排序:借助二分法的思想,使用递归,选定中间元素,将小于该值的元素放到左边,大于等于的放到右边
public class QuickSort {
public int[]quickSort(int[] A, int n) {
//特殊输入
if(A==null||A.length<=0)return A;
//调用递归方法进行二分和元素的移动,完成排序
this.adjust(A,0,A.length-1);
return A;
}
//写一个递归的分割方法,将数组二分,然后以此元素为界将数组进行移动,小于等于的位于左边,大于等于的位于右边
/*public voiddivide(int[] array,int start,int end){
//递归一定要写终止的边界条件
if(start==end)return;
//找任意一个元素作为分界点,但是通常选择中间元素作为分界点
int middle=(start+end)/2;
//对当前的数组根据中间点进行分界,使得小于等于的位于左边,大于的位于右边
this.adjust(array,start,end);
//继续对2个子数组进行二分
this.divide(array,start,middle);
this.divide(array,middle+1,end);
//调用adjust()方法将[start,end]范围内的数组以middle为界进行移动使之按照大小位于2边
this.adjust(array,start,end);
}
*/
//写一个方法adjust(),将[start,end]范围内的元素按照中间值分成2个部分并调整大小,小于等于的在左,大的在右
public voidadjust(int[] array,int start,int end){
//递归一定要写终止的边界条件,这里的边界条件是需要分界的数组只有一个元素
if(start==end)return;
//显然分界元素是middle
intmiddle=(start+end)/2;
//核心逻辑,将小于middle的元素放到左边,大于middle的元素放到右边
//①先将分界元素与最后的元素交换使得分界元素位于最后面
this.swap(array,middle,end);
//②设置指针p指向抽象的小于等于middle的数组smallArray{}456123;指针i用来遍历原始数组array,每次调整都是从0开始进行遍历调整
int p=start-1;
for(inti=start;i<=end;i++){
if(array[i]<=array[end]){
//如果元素i小于分界元素,那么就与小于等于数组区间{}的下一个元素进行交换
swap(array,++p,i);
}
}
/*
③本次调整结束,小于等于array[middle]值的元素全部位于数组的前列,大于的位于后面,注意,middle只是位置上面的中间,并不是大小的中间值(中位数),于是调整后的数组原来的分界值并不在中间位置,只是确保该值前面的都是小于该值的,该值后面的都是大于该值的而已。于是在此之后要继续对分界后的2个子数组进行分界排序,即对2个子数组调用递归过程。注意此时的2个子数组的分界位置是调整后的分界值所在的新位置,即上面循环过后的p的位置。于是对[start,p]和[p+1,end]进行调整
*///假设执行adjust()方法后就完成了调整功能,即将[start,end]范围的元素进行了分界,不要考虑递归细节
this.adjust(array,start,p);
this.adjust(array,p+1,end);
}
//专门写一个辅助函数用来交换数组中的2个元素
public voidswap(int[] array,int p1,int p2){
inttemp=array[p1];
array[p1]=array[p2];
array[p2]=temp;
}
}
对于快速排序,换成这种思路:
快速排序和归并排序一样,都采用分治的思想,先分再合,写一个divide(array,start,end)方法来对数组array中从start到end范围的数组进