交換排序——冒泡排序與快速排序

交換排序基本思想是:兩兩比較待排序列記錄的關鍵碼,發現記錄逆置則進行交換,直到沒有逆置對爲止。冒泡排序和快速排序是典型的交換排序算法。

冒泡排序

冒泡排序與直接插入排序也許是生活中我們最常用的排序方法,冒泡排序由兩層嵌套循環構成:外層循環每次確定一個位置,內層循環相對於外層,從相反方向與相鄰元素兩兩比較,直到外層當前位置,繼而得出對應的值。

動態演示圖如下:
冒泡排序
代碼實現如下(與動態演示圖排序方向相反):

import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args){
		// 待排序列
        int[] arrOriginal = new int[]{5, 9, 7, 4, 22, 2, 65, 1, 45};
        System.out.println("before sort, the array is: ");
        System.out.println(Arrays.toString(arrOriginal));

		// 臨時變量,內層循環中交換元素時應用
        int temp = 0;
        boolean noSwap;
        // 外層:從某一端開始,確定要排序的某個位置
        for (int i = 0; i < arrOriginal.length; i++) {
        	noSwap = true;
        	// 內層:從另一端開始,確定外層位置對應的值
            for (int j = arrOriginal.length - 1 ; j > i; j--) {
                if(arrOriginal[j] < arrOriginal[j-1]){
                    temp = arrOriginal[j];
                    arrOriginal[j] = arrOriginal[j-1];
                    arrOriginal[j-1] = temp;
                    noSwap = false;
                }
            }
            if(noSwap) {
                break;
            }
        }
        System.out.println("\nend sort, the array is: ");
        System.out.println(Arrays.toString(arrOriginal));
    }
}

與直接插入排序一樣,時間複雜度最優情況下爲O(n),平均和最差情況爲O(n2)。

快速排序

快速排序,1962年發明,體現了分治的思想:首先,選定軸值;然後,將待排序列元素分別與軸值比較,劃分到軸值兩側,軸值元素位置固定;最後,針對左右兩側遞歸調用快速排序算法,直到待排序列元素數小於2個。

動態演示圖如下:
快速排序算法
代碼實現如下:

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args){
        int[] arrOriginal = new int[]{5, 9, 7, 4, 22, 2, 65, 1, 45};
        System.out.println("before sort, the arr is: ");
        System.out.println(Arrays.toString(arrOriginal));

        quickSortProcess(arrOriginal, 0, arrOriginal.length-1);

        System.out.println("end sort, the arr is: ");
        System.out.println(Arrays.toString(arrOriginal));
    }

    public static void quickSortProcess(int[] arr, int startIdx, int endIdx) {
        if (startIdx >= endIdx){
            return;
        }
        // 1. 選擇軸值
        int flagIndex = (startIdx + endIdx) / 2;
        int flagValue = arr[flagIndex];

        
        // 2. 將軸值與末尾元素交換,確定一個可交換的元素
        switchVal(arr, flagIndex, endIdx);
        // 設定正向與反向遊標指針
        int forwardIdx = startIdx;
        int reverseIdx = endIdx;
        // while 正idx < 反idx
        while(forwardIdx < reverseIdx) {
            // 正向從起始元素開始,遇到比軸值大的元素時,與反向指針元素交換位置,正向指針暫停,break;否則正向+1
            while(forwardIdx < reverseIdx && arr[forwardIdx] < flagValue){
                forwardIdx++;
            }
            switchVal(arr, forwardIdx, reverseIdx);

            // 反向從結束元素開始,遇。。。。小。。。。,。正。。。。。。。。。,反向指針暫停,break;。。反向-1
            while(forwardIdx < reverseIdx && arr[reverseIdx] > flagValue){
                reverseIdx--;
            }
            switchVal(arr, forwardIdx, reverseIdx);
        }

        // 3. 二分,遞歸調用(註釋代碼爲錯誤示範,出現堆棧溢出的異常)
        // quickSortProcess(arr, startIdx, endIdx/2);
        // quickSortProcess(arr, (endIdx/2 + 1), endIdx);
        quickSortProcess(arr, startIdx, forwardIdx);
        quickSortProcess(arr, forwardIdx+1, endIdx);
    }

    public static void switchVal(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

快速排序時間複雜度爲:最差情況爲O(n2),最好與平均情況爲O(nlogn)

小結

冒泡與快速排序都基於交換排序的思路,提供了不同的實現方式,其中冒泡基於我們小數世界中的排序思路,每次挑出最大或最小,然後再從其餘數據項中找次大或次小的,因此它是穩定的排序,但這種直觀的思路其時間複雜度爲平方級別,並不適用於大數據量的情況。
快速排序則使用分治的思想,通過自頂向下的拆分,將排序問題縮減規模,在完成小規模排序的同時,最終實現了整體的排序效果,且執行更加高效。

參考資料:
《數據結構與算法》
Bubble sort - wikiwand
Quicksort - wikiwand

系列示例代碼下載:歡迎關注我的github

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