1. 程式人生 > 實用技巧 >Java資料結構與演算法之快速排序、歸併排序

Java資料結構與演算法之快速排序、歸併排序

7. 快速排序

7.1 快速排序思路

快速排序的基本思想是任取待排序序列的一個元素作為中心元素(可以用第一個,最後一個,也可以是中間任何一個),習慣將其稱為pivot,樞軸元素;
將所有比樞軸元素小的放在其左邊;
將所有比它大的放在其右邊;
形成左右兩個子表;
然後對左右兩個子表再按照前面的演算法進行排序,直到每個子表的元素只剩下一個。

可見快速排序用到了分而治之的思想。
將一個數組分成兩個陣列的方法為:
先從陣列右邊找到一個比樞軸元素小的元素,將陣列的第一個位置賦值為該元素;
再從陣列的左邊找到一個比樞軸元素大的元素,將從上面取元素的位置賦值為該值;
依次進行,直到左右相遇,把樞軸元素賦值到相遇位置。

總結:快排演算法可以看作是對氣泡排序的一種優化,其中採用了分而治之和遞迴的思想,極大的優化了時間複雜度。

7.2 檢視分析

第一步:確定左右指標位置和軸心數。

第二步:分別找到左右方不符合要求的數,構建中間變數進行交換。

第三步:左指標與右指標重合或左指標大於右指標,證明第一遍尋找結束,跳出迴圈。

第四步:分別進入左遞迴和右遞迴。

第五步:遞迴結束,得到的就是正確排序。

注意:遞迴的左邊界和右邊界問題。左遞迴的左邊界是陣列的左邊界,右邊界是第一遍迴圈結束時的右指標;右遞迴的右邊界是陣列的右邊界,左邊界是第一遍迴圈結束的左指標。這樣做是因為指標重合的位置已經確定,左右指標已分別錯開作為下一組新陣列的邊界條件。

7.2 程式碼實現

public static int[] quickSort(int[] nums,int left,int right) {
        int indexL = left; // 
        int indexR = right;
        int pivot = nums[(indexL+indexR)/2];
        int temp = 0;

        while (indexL < indexR) {
            while (nums[indexL] < pivot) {
                indexL += 1;
            }
            while (nums[indexR] > pivot) {
                indexR -= 1;
            }

            temp = nums[indexL];
            nums[indexL] = nums[indexR];
            nums[indexR] = temp;

            if (indexL >= indexR) {
                break;
            }
			
			// 有很多朋友對這個地方不太理解
			// 如果交換的數與軸心數一致,但又不進行移位的話,那麼一旦排序進入到最後環節,即這個數和軸心數進行排序會進入到死迴圈
			// 為什麼要對相反的指標進行移位呢?
			// 如何移動己方指標,那麼這個數就有可能留在己方陣列,而不會移動到正確的位置。
			// 這是因為重合的位置不會改變,左邊的陣列元素不會進入到右邊,右邊也不會進入到左邊,跳過這個相等數就極有可能將這個數留在了錯誤的位置上
           if (nums[indexL] == pivot) {
               indexR -= 1;
               System.out.println(nums[indexL]);
            }
            if (nums[indexR] == pivot) {
                indexL += 1;
                System.out.println(nums[indexR]);
            }
        }

        if (indexL == indexR) {
            indexL += 1;
            indexR -= 1;
        }

        if (indexL < right) {
            quickSort(nums,indexL,right);
        }
        if (left < indexR) {
            quickSort(nums,left,indexR);
        }
        return nums;
    }

8. 歸併排序

8.1 歸併排序介紹

歸併排序是利用歸併的思想實現的排序方法,該演算法採用經典的分治策略。

8.2 歸併排序之分

8.3 歸併排序之治

8.4 程式碼實現

public static void mergeSort(int[] nums, int left, int right, int[] temp) {
        if (left < right) {
            int mid = (left + right)/2;
            mergeSort(nums,left,mid,temp);
            mergeSort(nums,mid+1,right,temp);
            merge(nums,left,mid,right,temp);
        }
        //return nums;
    }

    public static void merge(int[] nums, int left, int mid, int right, int[] temp) {
        int i = left;
        int j = mid+1;
        int t = 0;

        while (i <= mid && j <= right) {
            if (nums[i] <= nums[j]) {
                temp[t] = nums[i];
                t += 1;
                i += 1;
            }else {
                temp[t] = nums[j];
                t += 1;
                j += 1;
            }
        }

        while (i <= mid) {
            temp[t] = nums[i];
            t += 1;
            i += 1;
        }
        while (j <= right){
            temp[t] = nums[j];
            t += 1;
            j += 1;
        }

        t = 0;
        int tempLeft = left;
        System.out.println("tempLeft=" + tempLeft + "right=" + right);
        while (tempLeft <= right) {
            nums[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }
    }

執行結果:

可以看出程式碼的具體排序順序與次數,這也驗證了分治的思想,先從大分小,再從小治大。

8.5 手寫分析

手寫分析圖也很好的證實了分治的思想與具體的遞迴路徑。