快排是很经典的排序算法。
百度百科显示:它是对冒泡排序的一种改进,一般来讲会可以为四个步骤:
- 选择基准值
- 将数组分为两部分,左部分的数值都小于或等于基准值,右部分的数值都大于或等于基准值,相当于这一步就确定了一个基准值的最终位置
- 递归对左右两侧的数据进行同样方法的排序
- 如果某次递归中排序部分的长度为1,就直接返回,不用操作
递归结束后,整个数组的排序就完成了!
在网上搜索代码的时候发现有多种写法,不考虑变体和优化,只看思想,我看到的主流的可以分为两种:
一种是上述过程的具体实现版本,首先通过partition操作得到基准值的所在位置,然后据此划分两个范围,递归执行。代码如下:参考链接
public void quickSort1(int[] nums,int l,int r){
if(l>=r) return;
int pivot = partition(nums,l,r);//得到基准数的位置
quickSort1(nums,l,pivot-1);//递归执行左范围
quickSort1(nums,pivot+1,r);/递归执行右范围
}
public int partition(int[] nums,int l,int r){
int pivot = nums[l];//基准数
while(l<r){
while(l<r && nums[r]>=pivot) r--;//找比pivot大的数
nums[l] = nums[r];
while(l<r && nums[l]<=pivot) l++;//找比pivot小的数
nums[r] = nums[l];//两次赋值,完成数值交换
}
nums[l] = pivot;//pivot的最终位置
return l;
}
另一种写法就是我之前看的某个网站上贴的模板,但现在找不到原地址了,原版是C++写的,Java就多写了一个swap操作,代码直接贴在这里:
public static void quickSort2(int[] nums,int l,int r){
if(l>=r) return;
int i=l-1,j=r+1,x=nums[l];
while(i<j){
do{i++;}while(nums[i]<x);
do{j--;}while(nums[j]>x);
if(i<j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
else break;
}
quickSort2(nums,l,j);
quickSort2(nums,j+1,r);
}
个人觉得这种写法更简单直观,但要注意的是,这种写法和前面提到的思路是不一样的,前一种相当于是分左中右的去做,但这一种就是只分左右,在对一个基准数操作完后,是无法保证该基准数到达了正确的最终位置的,但可以保证两个区间划分开来了,所以可以递归去执行两个区间。
这两种写法的区别可以在算法题数组中的第 k 大的数字 中深切体会到,这道题更适合用第一种,因为partition的过程其实就是不断缩小范围的过程,这样写就更直观,我们可以直接根据partion的结果去选择递归执行哪一侧,当然第二种写法也可以达到相同的目的,但是一定要注意两种写法细节上的区别!