一、原理
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据小,然后在按此方法对这两部分数据分别进行排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列的过程。
二、代码实现
package com.jdk8.SortTest;
public class QuickSort {
public static void display(int[] arrays){
for(int i = 0;i < arrays.length;i++){
System.out.print(" " + arrays[i] + " ");
}
}
public static void main(String[] args){
int[] arrays = {3,9,63,93,72,15,27,86};
System.out.println("排序前的数据为:");
display(arrays);
doQuickSort(arrays,0,arrays.length-1);
System.out.println();
System.out.println("排序后的数据为:");
display(arrays);
System.out.println();
}
private static void doQuickSort(int[] arrays,int left,int right) {
if(left >= right){
return;
}
int index = partition(arrays,left,right);
doQuickSort(arrays,left,index-1);
doQuickSort(arrays,index+1,right);
}
private static int partition(int[] arrays, int left, int right) {
int key = arrays[left];
while(right > left){
while(arrays[right] > key && right > left){
right = right - 1;
}
if(left != right){
arrays[left] = arrays[right];
}
while(arrays[left] <= key && left < right){
left = left + 1;
}
if(left != right){
arrays[right] = arrays[left];
}
}
arrays[right] = key;
return right;
}
}
三、复杂度分析
3.1、时间复杂度分析
快速排序时间复杂度为n*lg(n) ,推导过程如下所示:
f(n)来表示数据量为n时,算法的计算次数,很容易知道:
- 当n=1时,quick_sort函数只计算1次
f(1)=1【式子A】
在n很大时:
第一步,先做一次partition;
第二步,左半区递归;
第三步,右半区递归;
即:
f(n)=n+f(n/2)+f(n/2)=n+2*f(n/2)【式子B】
画外音:
(1)partition本质是一个for,计算次数是n;
(2)二分查找只需要递归一个半区,而快速排序左半区和右半区都要递归,这一点在*分治法与减治法*一章节已经详细讲述过;
【式子B】不断的展开,
f(n)=n+2*f(n/2)
f(n/2)=n/2+2*f(n/4)
f(n/4)=n/4+2*f(n/8)
…
f(n/2^(m-1))=n/2^(m-1)+2f(n/2^m)
上面共m个等式,逐步带入,于是得到
f(n)=n+2*f(n/2)
=n+2[n/2+2f(n/4)]=2n+4*f(n/4)
=2n+4[n/4+2f(n/8)]=3n+8f(n/8)
=…
=mn+2^mf(n/2^m)
再配合【式子A】:
f(1)=1
即,n/2^m=1时, f(n/2^m)=1, 此时m=lg(n), 这一步,这是分析这个算法的关键。
将m=lg(n)带入,得到:
f(n)=lg(n)n+2^(lg(n))f(1)=n*lg(n)+n
故,快速排序的时间复杂度是n*lg(n)。
3.2、空间复杂度
快速排序的临时变量所占用的空间不随处理数据n的大小改变而改变,即空间复杂度为O(1)。
四、稳定性
快速排序是一个不稳定的排序
例如(5,3A,6,3B)对这个进行排序,排序之前相同的数3A与3B,A在B的前面,经过排序之后会变成
(3B,3A,5,6),所以说快速排序是一个不稳定的排序