經典排序之快速排序

package 快速排序;

/*
 * 快排的思想:是一種分治策略,但不同於歸併的分治,歸併的分治是將數組分成相等的兩部分處理問題再最後歸併起來,
 * 而快排中間的分治是將數組分成不相等的兩部分,就是選取一個支點,然後將小於支點的數據全部都放在支點的左邊,將大於
 * 它的數據放在支點的右邊,完成一次快排,最後進行遞歸操作左右部分
 *
 * 其中有一些技巧:首先是選取支點,可以使用三點取中值支點選取法(三點即第一個,中間一個,最後一個)
 *            其次是將支點挪到數組的倒數第二位
 *            
 * 具體方法:首先是選取支點,如上,然後就是從左往右和從右往左的順序比較各個數據和支點的大小,
 * 左往右:令第一個索引是first+1,因爲在三點取值法中已經將第一個和中間一個還有最後一個排好序了,所以直接從第二個開始
 *         若比支點大就停下腳步,等着  從右往左 的數據  比支點小的數據,而且左邊索引比右邊索引小,然後進行交換
 *       如果不比支點大的話,就繼續向前進,注意當數字較小時使用另外的像插入排序之類的方法
 * 右往左同樣處理
 */
public class quickSort {
    public static <T extends Comparable<? super T>> void sort(T[] a, int first,
            int last) {

        int MIN_SIZE = 4;
        // 如果排序數量較小就選擇 插入排序哈!
        if (last - first + 1 < MIN_SIZE) {
            insertionSort(a, first, last);
        } else {
            // 創建劃分,找到支點: 較小部分|支點|較大部分
            int pivotIndex = partition(a, first, last);
            // 對較小部分和較大部分子數組排序 遞歸的思想
            sort(a, first, pivotIndex - 1);
            sort(a, pivotIndex + 1, last);
        }
    }

    // 找到支點的位置
    public static <T extends Comparable<? super T>> int partition(T[] a,
            int first, int last) {
        // 三者取中值支點選擇法 選擇中間的值作爲支點
        int mid = (first + last) / 2;
        if (a[first].compareTo(a[mid]) > 0)
            swap(a, first, mid);
        if (a[mid].compareTo(a[last]) > 0)
            swap(a, mid, last);
        if (a[first].compareTo(a[mid]) > 0)
            swap(a, first, mid);

        // 將支點移到數組的倒數第二個位置
        swap(a, mid, last - 1);
        int pivotIndex = last - 1;
        T pivot = a[pivotIndex];

        int indexFromLeft = first + 1;
        int indexFromRight = last - 2;

        boolean flag = false;
        while (!flag) {
            // 從左邊往右邊移動,當左邊值小於支點值時不移動
            while (a[indexFromLeft].compareTo(pivot) < 0) {
                indexFromLeft++;
            }
            // 從右邊往左邊移動,當右邊值大於支點值時不移動
            while (a[indexFromRight].compareTo(pivot) > 0) {
                indexFromRight--;
            }

            // 左邊索引<右邊索引 且左邊值大於支點,右邊值小於支點時
            if (indexFromLeft < indexFromRight) {
                swap(a, indexFromLeft, indexFromRight);
                indexFromLeft++;
                indexFromRight--;
            } else
                flag = true;

        }// end while()

        swap(a, pivotIndex, indexFromLeft);
        pivotIndex = indexFromLeft;

        return pivotIndex;

    }

    // 交換……
    private static void swap(Object[] arr, int i, int j) {
        Object item = arr[i];
        arr[i] = arr[j];
        arr[j] = item;
    }

    private static <T extends Comparable<? super T>> void insertionSort(T[] a,
            int first, int last) {
        for (int i = 1; i < a.length; i++) {
            int j = i - 1;
            T item = a[i];
            while (j >= 0&&a[j].compareTo(item) > 0) {
                a[j + 1] = a[j];
                j--;
            }
            a[j + 1] = item;
        }
    }

    // 測試
    public static void main(String[] args) {
        Integer[] a = {26,134,1246,242,643, 12, 2345, 13534, 242, 534, 24433, 42342, 535,};
        sort(a, 0, a.length - 1);
        for (int i = 0; i < a.length; i++)
            System.out.print(a[i] + ", ");
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章