快速排序---傻子都看懂了。

快速排序

程序代碼

package com.uplooking.bigdata.datastructure;

import java.util.Arrays;

public class QuickSort {

    public static void main(String[] args) {
        int[] arr = {8, -2, 3, 9, 0, 1, 7, 6};
        System.out.println("排序前:" + Arrays.toString(arr));
        quickSort(arr);
        System.out.println("排序後:" + Arrays.toString(arr));
    }

    public static void quickSort(int[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }

    /**
     * 快速排序
     *  是一種分而治之的思想,以某一個基準元素爲標準,將集合中比該基準元素小的都放到其左側,反之放到其右側。
     *  這樣我們就能夠找到該基準元素在該集合中所處的位置。
     *  同理,我們就能夠找到每一個元素在集合中恰當的位置。
     *  有點類似於二分查找。
     *  一般這個基準元素就是第一個元素。
     *  設置兩個指針,一個設置在開頭,一個設置末尾,從末尾開始找,找到一個比基本元素小的便停下來,然後
     *  從左側開始找,直到找到一個比基準元素大的元素,停下來,二者元素進行交換,直到兩個指針碰頭,本次循環結束
     *  指針指向的位置就是該基準元素在集合中應該所處的位置
     *  eg
     *      {8, -2, 3, 9, 0, 1, 7, 6}
     *  benchmark
     *  第一個bm=8
     *      end = length - 1 = 7
     *      start=0
     *      end--,我們發現6比8小,end指針停下來了,當前索引爲j=7
     *      start++,直到元素爲9的位置停下來,當前索引i=3
     *      將i和j所對應的元素進行交換
     *    {8, -2, 3, [6], 0, 1, 7, [9]}
     *                i             j
     * 循環繼續
     *    end--,到索引爲6的位置又停下來了
     *    start++,直到和end碰頭都沒有再找到比8大的元素,所以我們就能斷定,碰頭的這個位置就應該是8在該集合中應該在的位置
     *   交換8的索引和碰頭的索引
     *   {8, -2, 3, 6, 0, 1, 7, 9}
     *                       i=j=6
     * 交換:{7, -2, 3, 6, 0, 1, 8, 9}
     * 同理,我們可以在8左側重複上述操作,8的右側也可以重複上述操作
     * 使用遞歸調用的方式來完成集合的排序
     * @param arr
     */
    public static void quickSort(int[] arr, int low, int high) {
        if(low > high) {
            return;
        }
        // 默認以[low, high]中的arr[low]作爲基準值
        int index = arr[low];
        // 定義左指針
        int start = low;
        // 定義右指針
        int end = high;
        // 開始向基準值掃描,當start < end條件不滿足時
        // 說明指針碰頭,則需要將基準值與start進行交換
        // 即該基準值在整個元素集合中的位置已經確定
        // 其左邊的值比基準值小,其右邊的值比基準值大
        while (start < end) {
            // 按照前面算法的設計,先從右邊開始掃描,直到找到比基準值小的數再停下來
            // (下面循環的意義就是,在start < end的前提下,如果end位置的值比index值大或等於,就繼續往左邊找)
            while (start < end && arr[end] >= index) {
                end--;
            }
            // 財從左邊開始掃描,直到找到比基準值大的數再停下來
            // (下面循環的意義就是,在start < end的前提下,如果start位置的值比index值小或等於,就繼續往右邊找)
            while (start < end && arr[start] <= index) {
                start++;
            }
            if (start < end) {
                // 前面的循環結束後,如果start還是小於end
                // 那麼就交換arr[start]和arr[end]的值
                swap(arr, start, end);
            }
        }
        // 上面的循環結束後,start指針和end指針碰頭,交換arr[start]和基準值
        arr[low] = arr[start];
        arr[start] = index;
        // 交換後,arr[start]左邊比它小,右邊比它大,然後再以它爲基準
        // 左邊和右邊進行相同的操作
        quickSort(arr, low, start - 1);
        quickSort(arr, start + 1, high);
    }

    /**
     * 位操作
     * 按位與(&)
     * 1&1=1
     * 1&0=0
     * 0&1=0
     * 0&0=0
     * 異或(^)
     * (值不同)就取真(1),否則爲(0)
     * 1&1=0
     * 1&0=1
     * 0&1=1
     * 0&0=0
     * 非
     *
     * @param arr
     * @param i
     * @param j
     */
    private static void swap(int[] arr, int i, int j) {
        /*int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;*/
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

}

/*
交換a=3和b=5,不用第三方變量,效率最高
    方法一:
        a = a + b = 8
        b = a - b = (8 - 5) = 3
        a = a - b = (8 - 3) = 5
異或的方式
    a=3-->低8爲0000 0011
    b=5-->低8爲0000 0101

    a = a ^ b
         0000 0011
       ^ 0000 0101
       ---------------
         0000 0110 --->6
    b = a ^ b
         0000 0110
       ^ 0000 0101
       --------------
         0000 0011--->3
    a = a ^ b
         0000 0110
       ^ 0000 0011
      ---------------
         0000 0101--->5
*/

測試

執行結果如下:

排序前:[8, -2, 3, 9, 0, 1, 7, 6]
排序後:[-2, 0, 1, 3, 6, 7, 8, 9]

時間複雜度分析

1.在最快及平均情況下,時間複雜度爲O(nlog2n)。
最壞情況就是每次挑中的中間值不是最大就是最小,其時間複雜度爲O(n^2)。
說明:
平均的時間複雜度記住就好了。
但是最壞的情況確實是可以算出來的,
假設每次選的基準值都是最大的,那麼對於end的操作,一開始就找到比index值小的數,
而對於start的操作,則要一直循環n-1次才能讓循環停下來。

交換一次index值後,選擇的基準值又是最大的,那麼start的操作就是n-2次......

以此類推,直到排序完成,1 + 2 + ... + n - 1,所以數量級爲n^2,這意味着,數據如果是倒序的,使用上面的實現,那麼此時的時間複雜度就爲O(n^2).

2.快速排序法不是穩定排序法。
可以考慮,當基準值index與arr[start]值進行交換時,假設arr[start - 1] == index時,那麼可以看到,其確實是不穩定的。

3.快速排序法是平均運行時間最快的排序法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章