您的位置:首页 > 编程语言 > Java开发

排序算法(二):快速排序

2017-04-25 01:17 316 查看
快速排序 算法,最坏时间复杂度是O(n*n),期望时间复杂度是O(nlgn),并且隐含的常数因子非常小;而且快排是原址性的,因此是实际应用中最好的选择之一。

1. 快速排序的过程

快速排序采用分治的思想,对一个数组A[p, . r]进行快排可以分为如下三步:

分解: 对数组A[p, r],计算得到一个下标q,使得子数组A[p, q-1]中的每一个元素小于A[q],而子数组A[q+1, r]中的每一个元素都大于A[q]。

解决: 通过递归调用快速排序,对子数组A[p, q-1]和A[q+1, r]进行排序。

合并: 因为排序是原址的,所以不需要合并操作。

2. 分解过程的实现

快速排序最关键的部分就是分解部分。



实现是通过4个指针p、r、i、j将数组分成4部分,p是数组开始的指针,r既指向了结束的元素,同时也标记该元素为主元,该数组就是以主元为分界线,因此我们要做的就是把数组按照和主元的大小分为两组,i指针就是这两组的分界线,j指针从头到尾进行遍历,遍历过的元素,大的就放到i 指针的后面,小的就交换到i指针后面。当j指针遍历结束,将r指针指向的元素与i指针后面的元素交换,这样就完成了分解,而交换后的位置就是我们想要的q的值。下面是快排的示意:



3. 代码实现

import com.sunpro.java.Exchange;
import com.sunpro.java.RandomGenerator;
import static com.sunpro.java.Print.printArray;
public class QuickSort {
private final static boolean DEBUG = true;
private static int count = 0;
public static void main(String args[]) {
//获取一个长度为10,数值范围[0,,100]的随机数组;
int[] a = RandomGenerator.randGenerator(10,1000);
//打印数组
printArray(a);
//快速排序
quickSort(a, 0,a.length-1);
printArray(a);
}

/**
*快速排序
*@param A : the array to be sorted
*@param p :the start index of the array;
*@param r : the end index of the array to be sorted;
*/
public static void  quickSort(int[] A, int p, int r) {
if (p < r) {
//分解:将数组以q元素为界,划分为两部分,左小右大;
int q = partition(A, p, r);
//分治递归
quickSort(A, p, q-1);
quickSort(A, q+1, r);
}
}

private static int partition(int[] A, int p, int r){
if(DEBUG)
System.out.print(++count + ")" + p + "-" + r + ": ");
//将r元素取出做主元;
int x = A[r];
//i指针,左大右小;
int i = p -1;
for(int j = p; j <= r - 1; j++) {
if(A[j] <= x) {//如果比主元小,就交换到i之前的部分
i++;
Exchange.exchange(A, i, j);
}
}
//将主元与比主元大的部分的第一个元素交换;
Exchange.exchange(A, ++i, r);
//debug
if(DEBUG)
printArray(A);
return i;
}
}


运行结果:

//第一次运行
>java QuickSort
645 463 108 603 20 803 949 148 393 869
1)0-9: 645 463 108 603 20 803 148 393 869 949
2)0-7: 108 20 148 393 463 803 645 603 869 949
3)0-2: 108 20 148 393 463 803 645 603 869 949
4)0-1: 20 108 148 393 463 803 645 603 869 949
5)4-7: 20 108 148 393 463 603 645 803 869 949
6)6-7: 20 108 148 393 463 603 645 803 869 949
20 108 148 393 463 603 645 803 869 949
//第二次运行
>java QuickSort
367 886 286 754 569 472 695 999 51 877
1)0-9: 367 286 754 569 472 695 51 877 886 999
2)0-6: 51 286 754 569 472 695 367 877 886 999
3)1-6: 51 286 367 569 472 695 754 877 886 999
4)3-6: 51 286 367 569 472 695 754 877 886 999
5)3-5: 51 286 367 569 472 695 754 877 886 999
6)3-4: 51 286 367 472 569 695 754 877 886 999
7)8-9: 51 286 367 472 569 695 754 877 886 999
51 286 367 472 569 695 754 877 886 999


通过打印过程结果,可以很清晰的看出快速排序的整个过程了。

示例代码中导入了几个自定义的包,实现比较简单,这里先不展开描述了。

4. 快速排序的性能

快排的运行时间依赖于划分是否平衡。如果划分是平衡的,那么快排的性能与归并排序一样;如果不平衡,那么快排的性能就接近插入排序了。

对一个随机输入的数组进行快排时,其性能与最好情况(每次划分都是对半)和平衡情况(每次划分都是等比例的)一样,都是O(nlgn)。

对于某些特定的情况,我们的输入并不具有随机性,因此我们可以在算法中引入随机化。这里通过随机抽样技术实现。

/**
* 随机化排序
*/
private static int randPartition(int[] A, int p, int r){
int seed = rand.nextInt(r - p) + p;
if(DEBUG)
System.out.print(seed + ":");
Exchange.exchange(A, seed, r);
return partition(A, p, r);

}


其实就是每次排序数组的主元都不是固定的,而是随机产生的。这样就是的划分比较均衡。

运行结果

//采用普通快排运行结果
1 2 3 4 5 6 7 8 9 10
1:1)0-9: 1 2 3 4 5 6 7 8 9 10
2)2-9: 1 2 3 4 5 6 7 8 9 10
3)2-8: 1 2 3 4 5 6 7 8 9 10
4)2-7: 1 2 3 4 5 6 7 8 9 10
5)2-6: 1 2 3 4 5 6 7 8 9 10
6)2-5: 1 2 3 4 5 6 7 8 9 10
7)2-4: 1 2 3 4 5 6 7 8 9 10
8)2-3: 1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
//采用随机化快排运行效果
>javac QuickSort.java
>java QuickSort
1 2 3 4 5 6 7 8 9 10
0:1)0-9: 1 2 3 4 5 6 7 8 9 10
8:2)1-9: 1 2 3 4 5 6 7 8 9 10
2:3)1-7: 1 2 3 4 5 6 7 8 9 10
4:4)3-7: 1 2 3 4 5 6 7 8 9 10
5:5)5-7: 1 2 3 4 5 6 7 8 9 10
6:6)6-7: 1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  快速排序 算法 Java