學習筆記----快速排序的java實現及其改良

csdnYF發表於2016-05-24

快速排序演算法是我們學習資料結構必學的演算法之一,雖然在java中,對列表的排序可直接使用Collections.sort(List l) (這使用的是歸併排序演算法) ,對基本型別的陣列也有Arrays.sort()(這使用的是改進的快速排序演算法),但實現一個快速排序,仍能給自己很多啟發。
很多演算法書上都使用c或者c++實現快排演算法,但是作為一個寫慣了java的程式設計師,再去寫c或c++有著各種水土不服(沒有豐富的API庫幫忙),加上jvm虛擬機器效能的提高,也就可以用java回顧一下以前的快速排序演算法,也算為面試做個準備。 快速排序的平均時間複雜度能達到O(n*log n),最壞情況下 (元素基本有序,中軸選擇總是最大或最小 、元素,此時快速排序退化成氣泡排序)為O(n^2)。
本例中使用隨機選擇中軸元素的方式降低了最壞情況可能性,並在子陣列比較小(在書上推薦是5-15個元素即比較小)的時候停止快排,採用插入排序(最好情況既元素基本有序下,時間複雜度為O(n))完成整個List的排序。 有關快速排序的原理,演算法書上已經講的很清楚了,按照我個人的理解,快速排序就是如何將整個陣列通過選擇的中軸(key)劃分成兩個子陣列,並且滿足(key)這種關係。這就算完成一趟快排了,剩下的只需要遞迴(key)即可。 快排程式碼如下:

public class QuickSort {

    static List<Integer> list = new ArrayList<>();
    private static int END_QUICKSORT = 7;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        for (int i = 0; i < 100000; i++) {
            list.add((int) (Math.random() * 1000));
            //list.add(i);
        }
        long a1 = System.nanoTime();

        quickSort(list);

        long a2 = System.nanoTime();
        System.out.println(a2 - a1);
        /*for (Integer temp : list) {
            System.out.println(temp);
        }*/
    }

    public static void quickSort(List<Integer> list) {

        quick(list, 0, list.size() - 1);


        // 對快排後基本有序陣列進行一次插入排序
        insertSort(list);
    }

    private static void insertSort(List<Integer> list2) {
        // TODO Auto-generated method stub
        for (int i = 1; i < list2.size(); i++) {
            int v = list2.get(i);
            int j = i - 1;
            for (; j >= 0 && list2.get(j) > v; j--) {
                list2.set(j + 1, list.get(j));
            }
            list.set(j + 1, v);
        }
    }

    private static void quick(List<Integer> list, int low, int hight) {
        // TODO Auto-generated method stub
        if (low < hight) {
            int key = partition(list, low, hight); // 選擇中軸並對子陣列部分排序

            if (key - low > END_QUICKSORT)
                quick(list, low, key - 1); // 對左半部分繼續使用快排
            if (hight - key > END_QUICKSORT)
                quick(list, key + 1, hight); // 當(key,hight)範圍小到一定程度,不再進行快排
        }
    }

    private static int partition(List<Integer> list, int low, int hight) {
        // TODO Auto-generated method stub
        int rand = (int) (Math.random() * (hight - low)) + low;
        int key = list.get(rand);
        if (rand != low) {
            int temp = list.get(low);
            list.set(low, key);
            list.set(rand, temp);
        }
        while (low < hight) {
            while (low < hight && list.get(hight) >= key)
                hight--;
            list.set(low, list.get(hight));
            while (low < hight && list.get(low) <= key)
                low++;
            list.set(hight, list.get(low));
        }

        list.set(low, key);
        return low;
    }

}

然而比較下原快速排序和隨機化快排的程式碼執行速度發現兩者差別並不大,當然在電腦上還存在其它程式影響執行,不夠準確,所以這個時間值就不貼出來了。

在JDK中,對快速排序做了如下三個優化,有興趣的同學可以去看看Arrays.sort方法的原始碼。
1、就像上面提到的,對於元素數小於7(java內定值)的陣列,直接返回插入排序的結果(因為插入排序對基本有序陣列和簡單陣列能保證線性效能,所以一些O(n^2)的演算法不一定就不能使用)
2、使用三平均劃分法(陣列最左邊,最右邊,最中間的元素的中位數)找到中軸。
3、對於這一點我還不太理解,即是說:將和中軸相等的元素都放到陣列中間,如:4,3,1,15,15,15,23,34,21 . 這樣只需排序(4,3,1)和(23,34,21)這三個子陣列,對於重複元素多的陣列可減少問題規模。

相關文章