快速排序(QuickSort)是对冒泡排序(BubbleSort)的一种改进。排序效率在同为O(N*logN)的几种排序方法中效率较高,且快速排序算法是 分治策略的典型应用。时间复杂度: O(N*logN)。
版本一:
算法的关键部分是Partition过程,它实现了对子数组 A[p...r] 的原址重排。
/**
* 快速排序一,算法导论P95
* @author Administrator
*
*/
public class QuickSort {
public void quickSort(Integer[] array, int left, int right) {
if(left < right) {
int p = partition(array, left, right); //设置递归边界
quickSort(array, left, p-1); //递归处理左序列
quickSort(array, p+1, right); //递归处理右序列
}
}
/**
* 关键部分, 实现对子数组 array[p...r] 的原址重排
* @param array
* @param p
* @param r
* @return
*/
public int partition(Integer[] array, int left, int right) {
Integer x = array[right]; //x为主元,在序列中为最后一个元素
int p = left-1;
//从第一个循环到倒数第二个(r-1)元素,和主元进行比较
for(int j = left; j < right; j++) {
if(array[j] <= x) { // <= 表示由小到大排列
p++;
this.exchange(array, p, j); //把array[p]右边的元素和当前元素交换
}
}
//直到循环结束,整个序列就变成了三部分
//从array[left..p]是比主元小的元素,array[p+1..right-1]是比主元大的元素,array[right]则是主元
//划分的目的是将主元放在这两个序列的中间,此时p在小序列的最后一位,p+1则是大序列的第一位
//所以把主元(最后一位)和大序列的第一个元素交换
this.exchange(array, p+1, right);
return p+1;
}
/**
* 实现 array[a] 和 array[b] 两个元素的交换
* @param array
* @param a
* @param b
*/
public void exchange(Integer[] array, int a, int b) {
Integer temp = array[a];
array[a] = array[b];
array[b] = temp;
}
}
图解:
版本二(随机化):
版本二是版本一的加强。版本一在选取主元的时候,每次都选取最右边的元素。当序列为有序时,会发现划分出来的两个子序列一个里面没有元素,而另一个则只比原来少一个元素。为了避免这种情况,引入一个随机化量来破坏这种有序状态。
在随机化的版本二中,选取 a[left..right] 中的随机一个元素作为主元,然后再进行划分,就可以得到一个平衡的划分。
import java.util.Random;
/**
* 随机化的快速排序,并为100万的随机数测试
* @author Administrator
*
*/
public class RandomizedQuickSort {
public void randomizedQuickSort(Integer[] array, int left, int right) {
if(left < right){
int p = randomizedPartition(array, left, right); //设置递归边界
this.randomizedQuickSort(array, left, p-1); //递归处理左序列
this.randomizedQuickSort(array, p+1, right); //递归处理右序列
}
}
/**
* 随机化分组
* @param array
* @param left
* @param right
* @return
*/
public int randomizedPartition(Integer[] array, int left, int right) {
int random = this.randomNum(left, right); //生成一个随机数,即是主元所在位置
this.exchange(array, random, right); //将主元与最右边元素互换位置,这样就变成了之前快排的形式
Integer x = array[right]; // 现在最后一位又变成主元了,所有以下部分和版本一的partition()方法一样
int i = left-1;
for(int j = left; j < right; j++) {
if(array[j] <= x) {
i++;
this.exchange(array, i, j);
}
}
this.exchange(array, i+1,right);
return i+1;
}
/**
* 实现 array[a] 和 array[b] 两个元素的交换
* @param array
* @param a
* @param b
*/
public void exchange(Integer[] array, int a, int b) {
Integer temp = array[a];
array[a] = array[b];
array[b] = temp;
}
/**
* 在i到j的区间内随机生成一个数,其中i和j为序列的下标
* 该方法是为了随机找到一个元素作为主元
* @param i
* @param j
* @return
*/
public int randomNum(int i, int j) {
Random random = new Random(); // 使用Random函数产生随机数
return random.nextInt(j-i) + i; // random.nextInt(n)为产生的随机数的范围
}
public static void main(String[] args) {
Integer[] testArray = new Integer[1000000]; //建立待测试的数组,数组容量为100万
int len = testArray.length;
Random random = new Random();
for(int i=0; i<len; i++) {
testArray[i] = random.nextInt(1000000);
}
/**
for(int i=0; i<len; i++) {
System.out.println(testArray[i] + "");
}
*/
long start = System.currentTimeMillis();
RandomizedQuickSort qs = new RandomizedQuickSort();
qs.randomizedQuickSort(testArray, 0, testArray.length-1);
long end = System.currentTimeMillis();
/**
System.out.println("排序结果");
for(Integer i : testArray) {
System.err.print(i + "<");
}
*/
long time = end - start;
System.out.println("排序耗时:" + String.valueOf(time));
}
}
实验结果:
100w数字:
算法 | 第1次耗时 | 第2次耗时 | 第3次耗时 | 第4次耗时 | 第5次耗时 | 平均耗时 |
---|---|---|---|---|---|---|
普通快排 | 663ms | 728ms | 775ms | 701ms | 744ms | 722ms |
随机化版本快排 | 946ms | 978ms | 804ms | 1014ms | 886ms | 925ms |