1. 程式人生 > >深入理解快速排序(隨機快排、雙路快排、三路快排)

深入理解快速排序(隨機快排、雙路快排、三路快排)

快速排序可以說是20世紀最偉大的演算法之一了。相信都有所耳聞,它的速度也正如它的名字那樣,是一個非常快的演算法了。當然它也後期經過了不斷的改進和優化,才被公認為是一個值得信任的非常優秀的演算法。

本文將結合快速排序的三方面進行比較和深入解析。

這裡寫圖片描述

快速排序

這裡寫圖片描述

public class QuickSort {
    // 遞迴使用快速排序,對arr[l...r]的範圍進行排序
    public static void QuickSort(int[] arr,int l,int r){
        if(l>=r)
            return;
        int
p = partition(arr,l,r); QuickSort(arr,l,p-1); QuickSort(arr,p+1,r); } // 將陣列通過p分割成兩部分 // 對arr[l...r]部分進行partition操作 // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p] public static int partition(int[] arr, int l, int r) { swap(arr, l, (int) (Math.random() * (r - l + 1
)) + l); // 隨機快速排序 int v = arr[l]; int j = l; for(int i = j +1;i<=r;i++){ if(arr[i] < v){ j++; swap(arr,i,j); } } swap(arr,l,j); return j; } public static void swap(int[] arr,int i,int
j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 列印arr陣列的所有內容 public static void printArray(int[] arr) { for (int i = 0; i < arr.length; i++){ System.out.print( arr[i] ); System.out.print( ' ' ); } System.out.println(); return; } public static void main(String[] args){ int[] arr = {4,3,12,12}; QuickSort(arr,0,arr.length-1); printArray(arr); } }

雙路快速排序

若果陣列中含有大量重複的元素,則partition很可能把陣列劃分成兩個及其不平衡的兩部分,時間複雜度退化成O(n²)。這時候應該把小於v和大於v放在陣列兩端

這裡寫圖片描述

實際上把等於的部分分散到了陣列兩端

這裡寫圖片描述


public class QuickSort2Ways {

    // 雙路快速排序的partition
    // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
    private static int partition(int[] arr, int l, int r) {

        // 隨機在arr[l...r]的範圍中, 選擇一個數值作為標定點pivot
        swap(arr, l, (int) (Math.random() * (r - l + 1)) + l);

        int v = arr[l];

        // arr[l+1...i) <= v; arr(j...r] >= v
        int i = l + 1, j = r;
        while (true) {
            // 注意這裡的邊界, arr[i] < 0, 不能是arr[i] <= v
            // 思考一下為什麼?
            while (i <= r && arr[i] < v)
                i++;

            // 注意這裡的邊界, arr[j] > v, 不能是arr[j] >= v
            // 思考一下為什麼?
            while (j >= l + 1 && arr[j] > v)
                j--;

            // 對於上面的兩個邊界的設定, 有的同學在課程的問答區有很好的回答:)
            // 大家可以參考: http://coding.imooc.com/learn/questiondetail/4920.html
            // 答案:多了個等號的判斷也會造成兩棵子樹不平衡

            if (i > j)
                break;

            swap(arr, i, j);
            i++;
            j--;
        }

        swap(arr, l, j);
        return j;
    }

    // 遞迴使用快速排序,對arr[l...r]的範圍進行排序
    private static void QuickSort2Ways(int[] arr, int l, int r) {

        // 對於小規模陣列, 使用插入排序
        // if( r - l <= 15 ){
        //    InsertionSort.sort(arr, l, r);
        //    return;
        // }

        int p = partition(arr, l, r);
        QuickSort(arr, l, p - 1);
        QuickSort(arr, p + 1, r);
    }


    private static void swap(int[] arr, int i, int j) {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    // 列印arr陣列的所有內容
    public static void printArray(int[] arr) {

        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
            System.out.print(' ');
        }
        System.out.println();
        return;
    }

    // 測試 QuickSort
    public static void main(String[] args) {

        //雙路快速排序演算法也是一個O(nlogn)複雜度的演算法
        // 可以在1秒之內輕鬆處理100萬數量級的資料
        int[] arr = {4, 3, 12, 12};
        QuickSort2Ways(arr, 0, arr.length - 1);
        printArray(arr);
    }
}

三路快速排序

陣列分成三個部分,大於v 等於v 小於v

這裡寫圖片描述

這裡寫圖片描述


public class QuickSort3Ways {

    // 遞迴使用快速排序,對arr[l...r]的範圍進行排序
    private static void QuickSort3Ways(int[] arr, int l, int r){

        // 隨機在arr[l...r]的範圍中, 選擇一個數值作為標定點pivot
        swap( arr, l, (int)(Math.random()*(r-l+1)) + l );

        int v = arr[l];

        int lt = l;     // arr[l+1...lt] < v
        int gt = r + 1; // arr[gt...r] > v
        int i = l+1;    // arr[lt+1...i) == v
        while( i < gt ){
            if( arr[i] < v){
                swap( arr, i, lt+1);
                i ++;
                lt ++;
            }
            else if( arr[i] > v ){
                swap( arr, i, gt-1);
                gt --;
            }
            else{ // arr[i] == v
                i ++;
            }
        }

        swap( arr, l, lt );

        QuickSort3Ways(arr, l, lt-1);
        QuickSort3Ways(arr, gt, r);
    }


    private static void swap(int[] arr, int i, int j) {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    // 列印arr陣列的所有內容
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
            System.out.print(' ');
        }
        System.out.println();
        return;
    }

    // 測試 QuickSort
    public static void main(String[] args) {
        // 三路快速排序演算法也是一個O(nlogn)複雜度的演算法
        // 可以在1秒之內輕鬆處理100萬數量級的資料
        int[] arr = {4, 3, 12, 12};
        QuickSort3Ways(arr, 0, arr.length - 1);
        printArray(arr);
    }
}