1. 程式人生 > >白話經典演算法快速排序-快速搞定

白話經典演算法快速排序-快速搞定

      快速排序演算法和歸併排序,都是由於時間複雜度只有O(N*logN),得到大量使用。快速排序的核心思想就是分而治之,我每次處理只負責將一個元素的位置排序正確,其他就先不管。那分而治之,遞迴處理後,所有元素都回到有序的正確位置,排序就完成了。

      演算法的時間複雜度,N就是因為,需要給每個元素都進行一次調整排序。而logN的時間複雜度,就是給元素調整排序,需要進行拆半資料遞迴。

演算法的基本思想是:

1.先從數列中取出一個數作為基準數。需要進行N次。

2.基於基準數進行分割槽,分割槽後,基準數處於正確排序的位置,其他數字繼續亂序。處理過程就是將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。

3.再對左右區間重複第二步,直到各區間只有一個數。

直接看程式碼和註釋,後文有詳細介紹:

public int adJustArray(int[] arr, int left, int right) {
        int i = left;
        int j = right;
        int base = arr[i];//挖坑,將這個數先取出,待會移動i,j指標去填坑
        while (i < j) {//開始一次次遍歷,找到合適的數去填坑、以及挖坑

            //開始做最right邊找一個比坑小或者等於的數填進來
            while (i < j && arr[j] >= base) {//記住,這裡是用base,而不是arr[i]
                j--;//如果➡右指標的數比坑裡的數大,那就不交換,直接繼續移動尋找
            }
            //找到了一個arr[j]比坑小,或者說i等於j啦,跳出了while迴圈
            if (i < j) {//判斷一下,是i小於j,才賦值填坑
                arr[i] = arr[j];//這樣,j這邊就有一個坑,需要左指標移動,找一個比坑的數大的填進來
                i++;
            }

            //開始做最left邊移動,找一個比坑大的數填進去
            while (i < j && arr[i] < base) {//記住,這裡是用base,
                i++;
            }
            if (i < j) {
                arr[j] = arr[i];//將i填入j後,i這裡就是一個新的坑
                j--;//需要移動j來填   
            }
        }
        //while 迴圈結束時候,i==j
        //基準base就放入i位置這個坑,固定下來
        arr[i] = base;
        return i;
    }

    //分治法
    public void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            int newBase = adJustArray(arr, left, right);//執行一次挖坑調整排序,將newbase這個元素固定下來。
            //接下來,遞迴左邊的、右邊的數
            quickSort(arr, left, newBase - 1);
            quickSort(arr, newBase + 1, right);
        }
    }

    public static void main(String[] args) {
        int[] a = new int[]{6,23,57,2,8,6,9};
        QuickSort quickSort= new QuickSort();
        quickSort.quickSort(a,0,a.length-1);
        for (int i: a) {
            System.out.println(i);
        }
    }

借鑑挖坑填數+分治法:

以一個數組作為示例,取區間第一個數為基準數。

0

1

2

3

4

5

6

7

8

9

72

6

57

88

60

42

83

73

48

85

初始時,i = 0;  j = 9;   X = a[i] = 72

由於已經將a[0]中的數儲存到X中,可以理解成在陣列a[0]上挖了個坑,可以將其它資料填充到這來。

從j開始向前找一個比X小或等於X的數。當j=8,符合條件,將a[8]挖出再填到上一個坑a[0]中。a[0]=a[8]; i++;  這樣一個坑a[0]就被搞定了,但又形成了一個新坑a[8],這怎麼辦了?簡單,再找數字來填a[8]這個坑。這次從i開始向後找一個大於X的數,當i=3,符合條件,將a[3]挖出再填到上一個坑中a[8]=a[3]; j--;

陣列變為:

0

1

2

3

4

5

6

7

8

9

48

6

57

88

60

42

83

73

88

85

 i = 3;   j = 7;   X=72

再重複上面的步驟,先從後向前找,再從前向後找

從j開始向前找,當j=5,符合條件,將a[5]挖出填到上一個坑中,a[3] = a[5]; i++;

從i開始向後找,當i=5時,由於i==j退出。

此時,i = j = 5,而a[5]剛好又是上次挖的坑,因此將X填入a[5]。

陣列變為:

0

1

2

3

4

5

6

7

8

9

48

6

57

42

60

72

83

73

88

85

可以看出a[5]前面的數字都小於它,a[5]後面的數字都大於它。因此再對a[0…4]和a[6…9]這二個子區間重複上述步驟就可以了。

對挖坑填數進行總結

1.i =L; j = R; 將基準數挖出形成第一個坑a[i]。

2.j--由後向前找比它小的數,找到後挖出此數填前一個坑a[i]中。

3.i++由前向後找比它大的數,找到後也挖出此數填到前一個坑a[j]中。

4.再重複執行2,3二步,直到i==j,將基準數填入a[i]中。

照著這個總結很容易實現挖坑填數的程式碼。