快速排序及三向切分快排——java實現


快速排序也是一種分治算法。主要思想是選取一個切分點,將大於切分點的元素都放置到數組右側,小於切分點的元素都放置到數組左側;然后遞歸,再對切分點左側和右側分別排序。

歸並排序時遞歸在前,歸並在后,快速排序是切分在前,排序在后。

快速排序的運行時間在1.39nlogn的某個常數因子范圍之內,歸並排序也能做到這一點,但是快速排序更快,因為它的移動次數更少。

快速排序的關鍵點在於切分點的選取,對於正好逆序的情況,它的復雜度達到了n2,而且與歸並排序相比不穩定。

為了防止最壞情況的出現,一般在排序前先將元素隨機打亂。

 1 package 排序;
 2 
 3 import edu.princeton.cs.algs4.In;
 4 import edu.princeton.cs.algs4.StdOut;
 5 import edu.princeton.cs.algs4.StdRandom;
 6 /**
 7  * @author evasean www.cnblogs.com/evasean/
 8  */
 9 @SuppressWarnings("rawtypes")
10 public class Quick快速排序 {
11     private static int partition(Comparable[] a, int lo, int hi){
12         int i = lo;
13         int j = hi+1;
14         Comparable v = a[lo];//切分元素
15         while(true){
16             while(less(a[++i],v))
17                 if(i==hi) break;
18             while(less(v,a[--j]))
19                 if(j==lo) break;
20             if(i>=j) break;
21             exch(a,i,j);
22         }
23         exch(a,lo,j);//此時j<=i,且v > a[j],將切分元素v放入正確位置
24         return j;
25     }
26     public static void sort(Comparable[] a){
27         StdRandom.shuffle(a); //消除最壞的情況
28         sort(a,0,a.length-1);
29     }
30     private static void sort(Comparable[] a, int lo, int hi){
31         if(hi <= lo) return;
32         int j = partition(a,lo,hi);
33         sort(a,lo,j-1);
34         sort(a,j+1,hi);
35     }
36     
37     @SuppressWarnings("unchecked")
38     private static boolean less(Comparable v, Comparable w){
39         return v.compareTo(w) < 0;
40     }
41     private static void exch(Comparable[] a, int i, int j){
42         Comparable t = a[i];
43         a[i] = a[j];
44         a[j] = t;
45     }
46     private static void show(Comparable[] a){
47         for(int i=0; i<a.length; i++) StdOut.print(a[i] + " ");
48         StdOut.println();
49     }
50     public static boolean isSorted(Comparable[] a){
51         for(int i = 1; i < a.length; i++){
52             if(less(a[i],a[i-1])) return false;
53         }
54         return true;
55     }
56     public static void main(String[] args){
57         String[] a = new In().readAllStrings();
58         sort(a);
59         assert isSorted(a);
60         show(a);
61     }
62 }

 

實際應用中經常會出現大量重復元素的排序情況,而快速排序在面對重復元素時排序復雜度並沒有降低。Dijkstra提出的三向切分快速排序方法可以迅速降低這種情況下的復雜度,甚至有可能達到線性級別n,如荷蘭國旗問題(見我的另一篇文章)。其基本思想就是選取切分點v,從左到右遍歷,維護一個從前往后遍歷的位置點lt,使得a[0]~a[lt-1]都小於v,維護一個從前往后遍歷的位置點i,使得a[lt]~a[i-1]都等於v,維護一個從后往前的位置點,使得a[i]~a[gt]都大於v。

 1 /*
 2      * 三向切分的快速排序
 3      * a[lo...lt-1]中的元素都小於v
 4      * a[gt+1....hi]中的元素都大於v
 5      * a[lt...i-1]中的元素都等於v
 6      * a[i...gt]中的元素都還未確定,通過下面處理
 7      * 1. a[i]小於v,將a[lt]和a[i]交換,將lt和i加1
 8      * 2. a[i]大於v,將a[gt]和a[i]交換,將gt減1
 9      * 3. a[i]等於v,將i加1
10      * 這些操作都會保證數組元素不變且縮小gt-i的值,這樣循環才會結束
11      */
12     private static void sort3way(Comparable[] a, int lo, int hi){
13         if(hi <= lo) return;
14         int lt = lo;
15         int i = lo+1;
16         int gt = hi;
17         Comparable v = a[lo];
18         while(i <= gt){
19             int cmp = a[i].compareTo(v);
20             if(cmp<0) exch(a,lt++,i++);
21             else if(cmp>0) exch(a,i,gt--);
22             else i++;
23         }//現在a[lo...lt-1] < v=a[lt...gt]<a[gt+1...hi]
24         sort3way(a,lo,lt-1);
25         sort3way(a,gt+1,hi);
26     }

用這里的sort3way方法替代快速排序中的sort(Comparable[] a, int lo, int hi)即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM