快速排序 (分治算法)
基本概念
快速排序可能是应用最广泛的排序算法,适用于各种不同的输入数据且在一般应用中比其他排序都要快的多。
快速排序是一种分治的排序算法。它将一组数组分成两个子数组,将两部分独立地排序,当两个子数组都有序是整个数组也就自然有序了。
- 原地排序,只需要一个很小的辅助栈。
- 将长度为 N N N 的数组排序所需时间和 N l g N NlgN NlgN 成正比
步骤
快速排序的基本思想是
- 先从数列中取出一个数作为基准数
- 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
- 再对左右区间重复第二步,直到各区间只有一个数
算法
java普通版
public class Quick{
public void sort(int arr[],int low,int high) {
int l=low;
int h=high;
int povit=arr[low];
while(l<h) {
while(l<h&&arr[h]>=povit)
h--;
if(l<h){
arr[l]=arr[h];
l++;
}
while(l<h&&arr[l]<=povit)
l++;
if(l<h){
arr[h]=arr[l];
h--;
}
}
arr[l]=povit;
print(arr);
System.out.print("l="+(l+1)+"h="+(h+1)+"povit="+povit+"\n");
if(l-1>low)sort(arr,low,l-1);
if(h+1<high)sort(arr,h+1,high);
}
}
减少交换次数,提高效率版
private <TextendsComparable<?superT>> voidquickSort(T[]targetArr,intstart,intend){
int i=start,j=end;
Tkey=targetArr[start];
while(i<j){
/*按j--方向遍历目标数组,直到比key小的值为止*/
while(j>i&&targetArr[j].compareTo(key)>=0){
j--;
}
if(i<j){
/*targetArr[i]已经保存在key中,可将后面的数填入*/
targetArr[i]=targetArr[j];
i++;
}
/*按i++方向遍历目标数组,直到比key大的值为止*/
while(i<j&&targetArr[i].compareTo(key)<=0){
/*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/
i++;
}
if(i<j){
/*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/
targetArr[j]=targetArr[i];
j--;
}
}
/*此时i==j*/
targetArr[i]=key;//应加判断
/*递归调用,把key前面的完成排序*/
this.quickSort(targetArr,start,i-1);
/*递归调用,把key后面的完成排序*/
this.quickSort(targetArr,j+1,end);
//两个递归应加判断
}
例子
举个例子 x[6] = { 64,57,86,42,12,53 }
六个数排序
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
64 | 57 | 86 | 42 | 12 | 53 |
第一步,开始地址 i = 0
,结束地址 j = 5
,基准值 key = 64
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
64 | 64 | 57 | 86 | 42 | 12 | 53 |
第二步,从后往前找比 key 小的第一个数 x[5] = 53
, i = 0
, j = 5
,进行交换
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
64 | 53 | 57 | 86 | 42 | 12 | 64 |
第三步,从前往后找比 key 大的第一个数 x[2] = 86
, i = 2
, j = 5
,进行交换
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
64 | 53 | 57 | 64 | 42 | 12 | 86 |
开始重复前面两步
第四步,从 j = 5
开始由后往前找比 key 小的第一个数 x[4] = 12
, i = 2
, j = 5
,进行交换
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
64 | 53 | 57 | 12 | 42 | 64 | 86 |
第五步,从 i = 2
开始由前往后找比 key 大的第一个数 x[5] = 86
, i = 5
,j = 5
,由于 i >= j
使用第一轮结束
即 64
左边的数都比它小,右边的数都比它大
然后在分别对 [0,3] 和 [5,5] 进行上述操作即可
i = 0
, j = 3
, key = 53
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
53 | 53 | 57 | 12 | 42 | 64 | 86 |
从后往前 x[3] = 42
, i = 0
, j = 3
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
53 | 42 | 57 | 12 | 53 | 64 | 86 |
从前往后 x[1] = 57
, i = 1
, j = 2
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
53 | 42 | 53 | 12 | 57 | 64 | 86 |
从后往前 x[2] = 12
, i = 1
, j = 2
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
53 | 42 | 12 | 53 | 57 | 64 | 86 |
[0, 3] 交换完成 [5, 5] 只有一个数,不用交换,自然有序
然后在对 [0, 1] 和 [3,3] 进行操作
i = 0
, j = 1
, key = 42
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
42 | 42 | 12 | 53 | 57 | 64 | 86 |
从后往前 x[1] = 12
, i = 0
, j = 1
key | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
42 | 12 | 42 | 53 | 57 | 64 | 86 |
[0, 1] 交换完成,整个数组排序完成
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
12 | 42 | 53 | 57 | 64 | 86 |
总结
快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。