经典快排思路:
1.首先设定一个分界值,通过该分界值将数组分为左右两个部分。
2.左边部分是小于等于分界值的数据,右边部分是大于分界值的数据。
3.左边和右边递归调用方法:设定分界值,划分左边的是小于等于分界值的数据,右边是大于分界值的数据。
举例:4,6,2,0,3
1.选取最右边的3作为分界值,划分后数组的左边部分是【2,0】,数组的右边部分是【4,6】,3是在中间位置。
2.左边部分递归调用选取分界值,划分左右部分的操作:选取0作为临界值,没有左边部分,中间是0,右边部分是2,这样子左边就完成了排序【0,2】。
3.右边部分递归调用选取分界值,划分左右部分的操作,选取最右边的6作为分界值,左边部分是4,中间是6,没有中间部分,这样子右边就完成了排序【4,6】。
4.整体的排序就是【0,2,3,4,6】。
tips:我们选取的是最右边的数作为分界值。
代码实现:
public static void quickSort(int[] arr) {
if (arr==null||arr.length<2) {
return;
}
quickSort(arr,0,arr.length-1);
}
//递归调用快排
public static void quickSort(int[] arr,int l,int r) {
if(l<r) {
int[] p=sepatare(arr, l, r);
quickSort(arr,0,p[0]-1);
quickSort(arr,p[1]+1,r);
}
}
//具体的划分过程:左边区的边界是min,右边区的边界式max,返回的是中间相等区的左右下标,通过它获得下次递归的左右边界
public static int[] sepatare(int[] arr, int l, int r) {
int min=l-1;
int max=r+1;
while(l<max) {
if (arr[l]<arr[r]) {
swap(arr,++min,l++);
}
else if(arr[l]>arr[r]) {
swap(arr,--max,l);
}
else {
l++;
}
}
return new int[]{min+1,max-1};
}
//两个数字交换
public static void swap(int[] arr, int i, int j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
时间复杂度的分析:
1.如果分界值恰好落在数组中间,就是左右部分恰好是相等的,利用递归的master公式,最好情况下的时间复杂度是O(nlogn);
2.如果已经排好序的数组,最坏情况下的时间复杂度是O(n*2);
3.所以我们可以用随机快排法,随机选中数组中的一个数,和最右边的数字交换,再进行快排,这个是一个概率事件表达式,所以平均时间复杂度是O(nlogn),额外的空间复杂度是O(logn);
例题:荷兰国旗问题(快速排序)
给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的
右边。要求额外空间复杂度O(1),时间复杂度O(N)
public static int[] sepatare(int[] arr, int l, int r) {
int min=l-1;
int max=r+1;
while(l<max) {
if (arr[l]<arr[r]) {
swap(arr,++min,l++);
}
else if(arr[l]>arr[r]) {
swap(arr,--max,l);
}
else {
l++;
}
}
return new int[]{min+1,max-1};
}