排序算法(二):快速排序
2017-04-25 01:17
316 查看
快速排序 算法,最坏时间复杂度是O(n*n),期望时间复杂度是O(nlgn),并且隐含的常数因子非常小;而且快排是原址性的,因此是实际应用中最好的选择之一。
分解: 对数组A[p, r],计算得到一个下标q,使得子数组A[p, q-1]中的每一个元素小于A[q],而子数组A[q+1, r]中的每一个元素都大于A[q]。
解决: 通过递归调用快速排序,对子数组A[p, q-1]和A[q+1, r]进行排序。
合并: 因为排序是原址的,所以不需要合并操作。
实现是通过4个指针p、r、i、j将数组分成4部分,p是数组开始的指针,r既指向了结束的元素,同时也标记该元素为主元,该数组就是以主元为分界线,因此我们要做的就是把数组按照和主元的大小分为两组,i指针就是这两组的分界线,j指针从头到尾进行遍历,遍历过的元素,大的就放到i 指针的后面,小的就交换到i指针后面。当j指针遍历结束,将r指针指向的元素与i指针后面的元素交换,这样就完成了分解,而交换后的位置就是我们想要的q的值。下面是快排的示意:
运行结果:
通过打印过程结果,可以很清晰的看出快速排序的整个过程了。
示例代码中导入了几个自定义的包,实现比较简单,这里先不展开描述了。
对一个随机输入的数组进行快排时,其性能与最好情况(每次划分都是对半)和平衡情况(每次划分都是等比例的)一样,都是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
相关文章推荐
- C++总结笔记(八)排序算法之快速排序
- 排序算法之——快速排序(Java实现)
- 排序算法之快速排序
- 排序算法——快速排序
- 排序算法整理之快速排序
- Java数组排序算法,冒泡,选择,插入,快速排序
- php排序算法(冒泡排序,快速排序)
- 排序算法-(随机)快速排序(递归)
- 【排序算法】快速排序原理及Java实现
- 漫谈经典排序算法:三、冒泡排序 && 快速排序
- 【待思考】排序算法——快速排序
- 【常用排序算法】以最简单的方式理解快速排序
- 排序算法之快速排序
- 几个排序算法总结,从冒泡到快速排序
- 排序算法(五)——快速排序
- 【常用排序算法】快速排序(Java实现)
- 比快速排序还快的排序算法
- 排序算法复习(Java实现)(一): 插入,冒泡,选择,Shell,快速排序
- 基本排序算法之快速排序
- 排序算法(Java语言)——快速排序