快速排序可以说是使用最广的排序算法了,主要的特点是基于原地排序(不需要使用辅助数组,节省空间);其实对于长度为N的数组使用快速排序时间复杂度为 NlogN;在前几篇也一起讨论了其他的排序算法,都没能够把这两个特点结合起来。

 快速排序算法实现及优化(快速排序算法实现及优化策略) 排序 算法 优化 第1张

本文转载自微信公众号「贝塔学JAVA」,作者Silently9527。转载本文请联系贝塔学JAVA公众号。

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

程序员常用的IDEA插件:https://github.com/silently9527/ToolsetIdeaPlugin

完全开源的淘客项目:https://github.com/silently9527/mall-coupons-server

前言

快速排序可以说是使用最广的排序算法了,主要的特点是基于原地排序(不需要使用辅助数组,节省空间);其实对于长度为N的数组使用快速排序时间复杂度为 NlogN;在前几篇也一起讨论了其他的排序算法,都没能够把这两个特点结合起来。

快速排序思路

快速排序也是一种分治的排序算法,把数组划分为两个子数组,然后递归对子数组进行排序,最终保证整个数组有序。

算法思路:

  1. 随机选择一个切分元素,通常选择的是数组的第一个元素
  2. 从数组的左边开始扫描找出大于等于切分元素的值,从数组的右边开始扫描找出小于等于切分元素的值,交换这两个值
  3. 循环这个过程直到左右两个指针相遇,这样就排定了一个元素,保证了切分元素左边的值都是小于它的值,右边的元素都是大于它的值
  4. 递归这个过程,最终保证整个数组有序

算法实现

根据快速排序算法的思路,我们可以写出第一版实现:

  1. publicclassQuickSortimplementsSortTemplate{
  2. @Override
  3. publicvoidsort(Comparable[]array){
  4. quickSort(array,0,array.length-1);
  5. }
  6. privatevoidquickSort(Comparable[]array,intlo,inthi){
  7. if(lo>=hi){
  8. return;
  9. }
  10. intpartition=partition(array,lo,hi);
  11. quickSort(array,lo,partition-1);
  12. quickSort(array,partition+1,hi);
  13. }
  14. privateintpartition(Comparable[]array,intlo,inthi){
  15. inti=lo,j=hi+1;
  16. Comparableel=array[lo];
  17. while(true){
  18. while(less(array[++i],el)){
  19. if(i==hi){
  20. break;
  21. }
  22. }
  23. while(less(el,array[--j])){
  24. if(j==lo){
  25. break;
  26. }
  27. }
  28. if(i>=j){
  29. break;
  30. }
  31. exch(array,i,j);
  32. }
  33. exch(array,lo,j);
  34. returnj;
  35. }
  36. }

这段代码是实现快速排序的常规实现,考虑最糟糕的情况,假如需要排序的数组是已经有序的[1,2,3,4,5,6,7,8],执行快速排序的过程如图:

 快速排序算法实现及优化(快速排序算法实现及优化策略) 排序 算法 优化 第2张

对一个长度为N的数组,最糟糕的情况下需要递归N-1次,所以时间复杂度是O(n2),为了避免这种情况出现,我们来看下算法如何改进

算法改进

  • 保证随机性 为了避免最糟糕的情况出现,有两个办法,第一是在排序数组之前先随机打乱数组;第二是在partition方法中随机取切分元素,而不是固定取第一个,简单实现:
  1. privateintpartition(Comparable[]array,intlo,inthi){
  2. inti=lo,j=hi+1;
  3. intrandom=newRandom().nextInt(hi-lo)+lo;
  4. exch(array,lo,random);
  5. Comparableel=array[lo];
  6. while(true){
  7. while(less(array[++i],el)){
  8. if(i==hi){
  9. break;
  10. }
  11. }
  12. while(less(el,array[--j])){
  13. if(j==lo){
  14. break;
  15. }
  16. }
  17. if(i>=j){
  18. break;
  19. }
  20. exch(array,i,j);
  21. }
  22. exch(array,lo,j);
  23. returnj;
  24. }
  • 切换到插入排序 这点和归并排序一样,对于小数组的排序直接切换成插入排序
  1. privatevoidquickSort(Comparable[]array,intlo,inthi){
  2. if(lo>=hi){
  3. return;
  4. }
  5. if(hi-lo<5){//测试,小于5就切换到插入排序
  6. insertionSort(array,lo,hi);
  7. return;
  8. }
  9. intpartition=partition(array,lo,hi);
  10. quickSort(array,lo,partition-1);
  11. quickSort(array,partition+1,hi);
  12. }
  13. //插入排序
  14. privatevoidinsertionSort(Comparable[]array,intlo,inthi){
  15. for(inti=lo;i<=hi;i++){
  16. for(intj=i;j>lo&&less(array[j],array[j-1]);j--){
  17. exch(array,j,j-1);
  18. }
  19. }
  20. }

三向切分 当我们需要排序的数组中出现了大量的重复元素,我们实现的快速排序在递归的时候会遇到许多全部重复的子数组,我们的算法依然会对其进行切分,这里有很大的提升空间。

思路就是先随意选择一个切分元素(el),然后把数组切换成大于、等于、小于三个部分,一次递归可以排定所有等于切分元素的值;维护一个指针lt、gt,使得a[lo..lt-1]都小于切分元素,a[gt+1..hi]都大于切分元素;

  • 初始化变量:lt=lo, i=lo+1, gt=hi
  • if a[i] < el ; 交换a[i]与a[lt], i++, lt++
  • if a[i] > el ; 交换a[gt]与a[i], gt--
  • a[i] == el; i++

代码实现:

  1. publicclassQuick3waySortimplementsSortTemplate{
  2. @Override
  3. publicvoidsort(Comparable[]array){
  4. quickSort(array,0,array.length-1);
  5. }
  6. @SuppressWarnings("unchecked")
  7. privatevoidquickSort(Comparable[]array,intlo,inthi){
  8. if(lo>=hi){
  9. return;
  10. }
  11. intlt=lo,i=lo+1,gt=hi;
  12. Comparableel=array[lo];
  13. while(i<=gt){
  14. inttmp=el.compareTo(array[i]);
  15. if(tmp>0){
  16. exch(array,lt++,i++);
  17. }elseif(tmp<0){
  18. exch(array,i,gt--);
  19. }else{
  20. i++;
  21. }
  22. }
  23. quickSort(array,lo,lt-1);
  24. quickSort(array,gt+1,hi);
  25. }
  26. }

转载请说明出处
知优网 » 快速排序算法实现及优化(快速排序算法实现及优化策略)

发表评论

您需要后才能发表评论