算法題解:快速排序算法(維基百科版)

算法概述

快速排序(Quicksort),又稱劃分交換排序,簡稱快排,一種排序算法,最先由東尼·霍爾提出。在平均情況下,排序n個項目要O(n log n)次比較。在最壞情況下則須要O(n^2)次比較,但這種情況並不常見。java

事實上,快速排序O(n log n)一般明顯比其餘算法更快,由於它的內部循環能夠在大部分的架構上頗有效率地達成。算法

算法流程

快速排序使用分治法策略來把一個序列分爲較小和較大的2個子序列,而後遞歸地排序兩個子序列。數組

步驟爲:架構

  1. 挑選基準值:從數列中挑出一個元素,稱爲「基準」(pivot),函數

  2. 分割:從新排序數列,全部比基準值小的元素擺放在基準前面,全部比基準值大的元素擺在基準後面(與基準值相等的數能夠到任何一邊)。在這個分割結束以後,對基準值的排序就已經完成,性能

  3. 遞歸排序子序列:遞歸地將小於基準值元素的子序列和大於基準值元素的子序列排序。ui

遞歸到最底部的判斷條件是數列的大小是零或一,此時該數列顯然已經有序。指針

選取基準值有數種具體方法,此選取方法對排序的時間性能有決定性影響。code

算法步驟

快速排序的算法步驟我給劃分爲5步:排序

  1. 遞歸終止條件
  2. 定義頭尾指針與基準值
  3. 把小於基準數的移到左邊,把大於基準數的移到右邊
  4. 繼續處理左邊的
  5. 繼續處理右邊的

示例代碼

public class QuickSort {
  
    public static void qSort(int[] arr, int head, int tail) {
        if (head >= tail || arr == null || arr.length <= 1) {
            return;
        }
        //定義倆指針 用於移動
        int left = head;
        int right = tail;
        int pivot = arr[head]; //基準值,也能夠arr[(head + tail) / 2]

        while (left <= right) {
            while (arr[left] < pivot) { //左指針先走,找到大於等於基準數的中止
                ++left;
            }
            while (arr[right] > pivot) { //右指針後走,找到小於等於基準數的中止
                --right;
            }
            if (left < right) {
                //交換arr[left]和arr[right]的位置
                int t = arr[left];
                arr[left] = arr[right];
                arr[right] = t;
                //繼續遍歷
                ++left;
                --right;
            } else if (left == right) {
                //遍歷完,錯開兩指針
                ++left;
                //break;
            }
        }

        qSort(arr, head, right);
        qSort(arr, left, tail);
    }

    public static void main(String[] args) {
        int[] arr = new int[]{6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        qSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }
  
}

打印輸出:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

在程序運行中,能夠經過打印排序前和排序後的數列觀察運行流程

對數組 [6 1 2 7 9 3 4 5 10 8 ] 排序, 排序後 [5 1 2 4 3 9 7 (6) 10 8 ] 
對數組 [5 1 2 4 3 ] 排序, 排序後 [3 1 2 4 (5) 9 7 6 10 8 ] 
對數組 [3 1 2 4 ] 排序, 排序後 [2 1 (3) 4 5 9 7 6 10 8 ] 
對數組 [2 1 ] 排序, 排序後 [1 (2) 3 4 5 9 7 6 10 8 ] 
對數組 [3 4 ] 排序, 排序後 [1 2 (3) 4 5 9 7 6 10 8 ] 
對數組 [9 7 6 10 8 ] 排序, 排序後 [1 2 3 4 5 8 7 6 10 (9) ] 
對數組 [8 7 6 ] 排序, 排序後 [1 2 3 4 5 6 7 (8) 10 9 ] 
對數組 [6 7 ] 排序, 排序後 [1 2 3 4 5 (6) 7 8 10 9 ] 
對數組 [10 9 ] 排序, 排序後 [1 2 3 4 5 6 7 8 9 (10) ]

複雜度分析

時間複雜度

數組有n個元素,由於要遞歸運算,算出支點pivot的位置,而後遞歸調用左半部分和有半部分,這個時候理解上是若第一層的話就是n/二、n/2,如果第二層就是n/四、n/四、n/四、n/4這四部分,即n個元素理解上是一共有幾層2^k=n,k=log2(n),而後每層都是n的複雜度,那麼平均就是O(nlog2 n)的時間複雜度。但這種確定是平均狀況,若是你是標準排序的狀況下,若是已是升序的順序,那麼遞歸只存在右半部分了,左半部分都被淘汰了。(n-1)(n-2)....*1,這個複雜度確定就是O(n^2)

空間複雜度

快排的實現是遞歸調用的, 並且每次函數調用中只使用了常數的空間,所以空間複雜度等於遞歸深度。

最好的狀況最多須要O(log2 n)次的嵌套遞歸調用,因此它須要O(log2 n)的空間。最壞狀況下須要O(n)次嵌套遞歸調用,所以須要O(n)的空間。

時間複雜度引用自知乎

https://www.zhihu.com/question/22393997/answer/189896378

相關文章
相關標籤/搜索