快速排序實現之遞歸與非遞歸

1、算法思想:

  • 快速排序是C.R.A.Hoare於1962年提出的一種劃分交換排序。它採用了一種分治的策略,一般稱其爲分治法(Divide-and-ConquerMethod)。
  • 設當前待排序的無序區爲R[low..high],利用分治法可將快速排序的基本思想描述爲:
    • 在R[low..high]中任選一個記錄做爲基準(Pivot),以此基準將當前無序區劃分爲左、右兩個較小的子區間R[low..pivotpos-1)和R[pivotpos+1..high],
    • 並使左邊子區間中全部記錄的關鍵字均小於等於基準記錄(不妨記爲pivot)的關鍵字pivot.key,右邊的子區間中全部記錄的關鍵字均大於等於pivot.key,而基準記錄pivot則位於正確的位置(pivotpos)上。
    • 經過遞歸調用快速排序對左、右子區間R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。

2、算法實現:

遞歸實現:

private static void rec_quickSort(int[] a, int start, int end) {
    int index = 0;
    if(start < end) {
        index = partition(a,start,end);
        rec_quickSort(a,start,index-1);
        rec_quickSort(a,index+1,end);
    }
}

private static int partition(int[] a, int start, int end) {
    int pivot = a[start];
    while(start < end) {
        while(start < end && a[end] >= pivot)
            end--;
        a[start] = a[end];
        while(start < end && a[start] <= pivot)
            start++;
        a[end] = a[start];
    }
    a[start] = pivot;
    return start;
}

非遞歸實現:

private static void nonRec_quickSort(int[] a,int start,int end) {
    LinkedList<Integer> stack = new LinkedList<Integer>();  //用棧模擬
    if(start < end) {
        stack.push(end);
        stack.push(start);
        while(!stack.isEmpty()) {
            int l = stack.pop();
            int r = stack.pop();
            int index = partition(a, l, r);
            if(l < index - 1) {
                stack.push(index-1);
                stack.push(l);
            }
            if(r > index + 1) {
                stack.push(r);
                stack.push(index+1);
            }
        }
    }
}

private static int partition(int[] a, int start, int end) {
    int pivot = a[start];
    while(start < end) {
        while(start < end && a[end] >= pivot)
            end--;
        a[start] = a[end];
        while(start < end && a[start] <= pivot)
            start++;
        a[end] = a[start];
    }
    a[start] = pivot;
    return start;
}

3、算法分析:

  • 快速排序的時間主要耗費在劃分操做上,對長度爲k的區間進行劃分,共需k-1次關鍵字的比較。

最壞時間複雜度

  • 最壞狀況是每次劃分選取的基準都是當前無序區中關鍵字最小(或最大)的記錄,劃分的結果是基準左邊的子區間爲空(或右邊的子區間爲空),而劃分所得的另外一個非空的子區間中記錄數目,僅僅比劃分前的無序區中記錄個數減小一個。
  • 所以,快速排序必須作n-1次劃分,第i次劃分開始時區間長度爲n-i+1,所需的比較次數爲n-i(1≤i≤n-1),故總的比較次數達到最大值:Cmax= n(n-1)/2=O(n2)

最好時間複雜度

  • 在最好狀況下,每次劃分所取的基準都是當前無序區的"中值"記錄,劃分的結果是基準的左、右兩個無序子區間的長度大體相等。總的關鍵字比較次數:0(nlgn)
  • 注意:用遞歸樹來分析最好狀況下的比較次數更簡單。由於每次劃分後左、右子區間長度大體相等,故遞歸樹的高度爲O(lgn),而遞歸樹每一層上各結點所對應的劃分過程當中所須要的關鍵字比較次數總和不超過n,故整個排序過程所須要的關鍵字比較總次數C(n)=O(nlgn)。
  • 由於快速排序的記錄移動次數不大於比較的次數,因此快速排序的最壞時間複雜度應爲0(n2),最好時間複雜度爲O(nlgn)。

空間複雜度

  • 快速排序在系統內部須要一個棧來實現遞歸。若每次劃分較爲均勻,則其遞歸樹的高度爲O(lgn),故遞歸後需棧空間爲O(lgn)。
  • 最壞狀況下,遞歸樹的高度爲O(n),所需的棧空間爲O(n)。

穩定性

  • 快速排序是非穩定的。
相關文章
相關標籤/搜索