快速排序思想+算法实现(JAVA)

什么是快速排序?

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。



算法原理

单单看以上解释还是有些模糊,可以通过实例来理解它,下面通过一组数据来进行排序过程的解析:

原数组:{3,7,2,9,1,4,6,8,10,5}

期望结果:{1,2,3,4,5,6,7,8,9,10}


花了点时间撸了下面这张快速排序示意图





Demo步骤解析:

1.一开始选定数组的最后一个元素5作为基准值,也就是最终排序结果应该是以5为界限划分为左右两边。

2.从左边开始,寻找比5大的值,然后与5进行调换(因为如果比5小的值本来就应该排在5前面,比5大的值调换之后就去到了5的后面),一路过来找到了7,将7与5调换,结束此次遍历。

3.从右边开始,由于7已经是上一轮排好序的便不再动它,从10开始,一路向左遍历,寻找比5小的值,然后与5进行调换(因为如果比5大的值本来就应该排在5后面,比5小的值调换之后就去到了5的后前面),一路过来找到了4,将4与5调换,结束此次遍历。

4.从左边开始,由于3和4都是前两轮已经排好序的便不再动它,从2开始,一路向右遍历,寻找比5大的值,然后与5进行调换(道理同步骤2),一路过来找到了9,将9与5调换,结束此次遍历。

5.从右边开始,由于排在9后面的那几个数字都是上几轮排好序的便不再动它,从1开始,一路向右遍历,寻找比5小的值,然后与5进行调换(道理同步骤3),一下子就找到了1,将1与5调换,结束此次遍历。

6.这个时候,发现5的左右两边都是排好序了的,所以结束此轮排序,5的左右两边抽出来各自进行下一轮的排序,规则同上,直到无法再拆分下去,即完成了整体的快速排序。



算法实现

既然思路理清了,代码就容易上手了:

[java]  view plain  copy
  1. /** 
  2.  * 快速排序 
  3.  * @author IT_ZJYANG 
  4.  */  
  5. public class QuickSort {  
  6.       
  7.     /** 
  8.      * 将数组的某一段元素进行划分,小的在左边,大的在右边 
  9.      * @param a 
  10.      * @param start 
  11.      * @param end 
  12.      * @return 
  13.      */  
  14.     public static int divide(int[] a, int start, int end){  
  15.         //每次都以最右边的元素作为基准值  
  16.         int base = a[end];  
  17.         //start一旦等于end,就说明左右两个指针合并到了同一位置,可以结束此轮循环。  
  18.         while(start < end){  
  19.             while(start < end && a[start] <= base)  
  20.                 //从左边开始遍历,如果比基准值小,就继续向右走  
  21.                 start++;  
  22.             //上面的while循环结束时,就说明当前的a[start]的值比基准值大,应与基准值进行交换  
  23.             if(start < end){  
  24.                 //交换  
  25.                 int temp = a[start];  
  26.                 a[start] = a[end];  
  27.                 a[end] = temp;  
  28.                 //交换后,此时的那个被调换的值也同时调到了正确的位置(基准值右边),因此右边也要同时向前移动一位  
  29.                 end--;  
  30.             }     
  31.             while(start < end && a[end] >= base)  
  32.                 //从右边开始遍历,如果比基准值大,就继续向左走  
  33.                 end--;  
  34.             //上面的while循环结束时,就说明当前的a[end]的值比基准值小,应与基准值进行交换  
  35.             if(start < end){  
  36.                 //交换  
  37.                 int temp = a[start];  
  38.                 a[start] = a[end];  
  39.                 a[end] = temp;  
  40.                 //交换后,此时的那个被调换的值也同时调到了正确的位置(基准值左边),因此左边也要同时向后移动一位  
  41.                 start++;  
  42.             }     
  43.               
  44.         }  
  45.         //这里返回start或者end皆可,此时的start和end都为基准值所在的位置  
  46.         return end;  
  47.     }  
  48.   
  49.     /** 
  50.      * 排序 
  51.      * @param a 
  52.      * @param start 
  53.      * @param end 
  54.      */  
  55.     public static void sort(int[] a, int start, int end){  
  56.         if(start > end){  
  57.             //如果只有一个元素,就不用再排下去了  
  58.             return;  
  59.         }   
  60.         else{  
  61.             //如果不止一个元素,继续划分两边递归排序下去  
  62.             int partition = divide(a, start, end);  
  63.             sort(a, start, partition-1);  
  64.             sort(a, partition+1, end);  
  65.         }  
  66.               
  67.     }  
  68.       
  69. }  



测试:

[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.           
  3.     int[] a = new int[]{2,7,4,5,10,1,9,3,8,6};  
  4.     int[] b = new int[]{1,2,3,4,5,6,7,8,9,10};  
  5.     int[] c = new int[]{10,9,8,7,6,5,4,3,2,1};  
  6.     int[] d = new int[]{1,10,2,9,3,2,4,7,5,6};  
  7.           
  8.     sort(a, 0, a.length-1);  
  9.           
  10.     System.out.println("排序后的结果:");  
  11.     for(int x : a){  
  12.         System.out.print(x+" ");  
  13.     }  
  14. }  



打印结果:




算法优缺点

快速排序最“快”的地方在于左右两边能够快速同时递归排序下去,所以最优的情况是基准值刚好取在无序区的中间,这样能够最大效率地让两边排序,同时最大地减少递归划分的次数。此时的时间复杂度仅为O(NlogN)
快速排序也有存在不足的情况,当每次划分基准值时,得到的基准值总是当前无序区域里最大或最小的那个元素,这种情况下基准值的一边为空,另一边则依然存在着很多元素(仅仅比排序前少了一个),此时时间复杂度为O(N*N)



快速排序的速度快慢关键在于基准值的选取,它决定了划分次数以及比较次数,决定了快排的效率,因此,还有一些针对于基准值选取的优化方法,例如“三数据取中法”等,能够有效优化快速排序存在的不足之处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值