1. 程式人生 > 實用技巧 >排序演算法之快速排序

排序演算法之快速排序

一、演算法思想

  快速排序(Quick Sort)使用分治法策略。它的基本思想是:選擇一個基準數,通過一趟排序將要排序的資料分割成獨立的兩部分;其中一部分的所有資料都比另外一部分的所有資料都要小。然後,再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。

二、演算法流程

(1) 從數列中挑出一個基準值。

(2) 將所有比基準值小的擺放在基準前面,所有比基準值大的擺在基準的後面(相同的數可以到任一邊);在這個分割槽退出之後,該基準就處於數列的中間位置。

(3) 遞迴地把"基準值前面的子數列"和"基準值後面的子數列"進行排序。

三、演算法實現

 1 //快速排序
 2     static class QuickSort implements Sort {
 3         @Override
 4         public String sortName() {
 5             return "快速排序";
 6         }
 7         @Override
 8         public Comparable[] sort(Comparable[] data) {
 9             int lo = 0, hi = data.length - 1;
10             sort(data, lo, hi);
11 return data; 12 } 13 //遞迴體 14 private static void sort(Comparable[] data, int lo, int hi) { 15 if(hi <= lo) return; 16 int partition = partition(data, lo, hi); 17 sort(data, lo, partition - 1); 18 sort(data, partition + 1, hi);
19 } 20 //分組 21 private static int partition(Comparable[] data, int lo, int hi) { 22 //確定分界值 23 Comparable key = data[lo]; 24 //定義兩個指標,分別指向待切分元素的最小索引處和最大索引處的下一個位置 25 int left = lo, right = hi + 1; 26 //切分 27 while(true) { 28 //先從左往右掃描,移動right指標,找到一個比分界值小的元素,停止 29 while(!Sort.greater(key, data[--right])) { 30 if(right == lo) break; 31 } 32 //再從左往右掃描,移動left指標,找到一個比分界值大的元素,停止 33 while(!Sort.greater(data[++left], key)) { 34 if(left == hi) break; 35 } 36 //判斷left>=right,如果是,則證明元素掃描完畢,結束迴圈,如果不是,則交換元素即可 37 if(left >= right) { 38 break; 39 } else { 40 Sort.swap(data, left, right); 41 } 42 } 43 //交換分界值 44 Sort.swap(data, lo, right); 45 return right; 46 } 47 }

四、演算法分析

複雜度

因為快速排序有兩個主要操作,所以分析時間複雜度的時候咱們也是從這兩個操作著手。

首先是切割操作,對於長度為的佇列,切割操作有兩種可能:

  1. :不執行切割操作,所以時間複雜度為常數 1
  2. :執行切割操作,因為次比較,所以時間複雜度為

接著是遞迴操作,設對於長度為的佇列遞迴操作的時間複雜度為,執行切割操作後其中一個子佇列的長度為。

那麼根據遞迴操作的邏輯,咱們可以得出遞迴操作的時間複雜度計算公式:

分析完兩個主要操作的時間複雜度之後,咱們就可以開始從整體上分析快排的時間複雜度了。快排的時間複雜度分析分三種情況:最壞、最好和平均。

    1. 最壞:每次都只切割出一個子佇列,即。這時候每次遞迴操作的時間複雜度,所以對於長度為的佇列來說,快排最壞情況下的時間複雜度,即
    2. 最好:每次都對半切割,即,總共會執行次遞迴。這時候每次遞迴操作的時間複雜度,而,代入可得

      ……

      所以最好情況下快排的時間複雜度為
    3. 平均:每次都隨機切割,即。這時候每次遞迴操作的時間複雜度
      ,最後得出時間複雜度約為。

穩定性

快速排序出於效能上的考慮,犧牲了演算法的穩定性。雖然可以改變交換規則,使得演算法保持穩定性,但卻犧牲了效能,對於一個排序演算法而言,這是得不償失的,除非是上下文有穩定性方面的需求,否則,不建議改變交換規則。