八大基礎排序

z_coding發表於2024-05-28

八大基礎排序

1. 氣泡排序(Bubble Sort)

基本思想:依次比較相鄰的兩個元素,若它們的順序錯誤則進行交換。
特點:穩定排序,但效率較低,時間複雜度為O(n^2),空間複雜度為O(1)。

程式碼例項

public class BubbleSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用氣泡排序對陣列進行排序  
        bubbleSort(arr);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 氣泡排序方法  
    public static void bubbleSort(int[] arr) {  
        int n = arr.length;  
        for (int i = 0; i < n - 1; i++) {  
            boolean swapped = false; // 標記位,表示這一趟是否有交換  
            for (int j = 0; j < n - i - 1; j++) {  
                // 交換如果元素找到比下一個元素大  
                if (arr[j] > arr[j + 1]) {  
                    // 交換元素  
                    int temp = arr[j];  
                    arr[j] = arr[j + 1];  
                    arr[j + 1] = temp;  
                    // 如果有交換,則標記為true  
                    swapped = true;  
                }  
            }  
            // 如果沒有交換髮生,說明陣列已經有序  
            if (!swapped) {  
                break;  
            }  
        }  
    }  
}

2. 選擇排序(Selection Sort)

基本思想:在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然後再從剩餘未排序元素中繼續尋找最小(或最大)元素,然後放到已排序序列的末尾。
特點:不穩定排序,時間複雜度為O(n^2),空間複雜度為O(1)。

例項程式碼

public class SelectionSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用選擇排序對陣列進行排序  
        selectionSort(arr);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 選擇排序方法  
    public static void selectionSort(int[] arr) {  
        int n = arr.length;  
        for (int i = 0; i < n - 1; i++) {  
            // 找到剩餘部分中的最小元素  
            int minIndex = i;  
            for (int j = i + 1; j < n; j++) {  
                if (arr[j] < arr[minIndex]) {  
                    minIndex = j;  
                }  
            }  
            // 將找到的最小元素與第一個未排序的元素交換  
            if (minIndex != i) {  
                int temp = arr[minIndex];  
                arr[minIndex] = arr[i];  
                arr[i] = temp;  
            }  
        }  
    }  
}

3. 插入排序(Insertion Sort)

基本思想:將待排序的資料分成已排序和未排序兩部分,每次將一個元素插入到已排序部分的正確位置。
特點:穩定排序,時間複雜度為O(n^2),空間複雜度為O(1)。

例項程式碼

public class InsertionSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用插入排序對陣列進行排序  
        insertionSort(arr);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 插入排序方法  
    public static void insertionSort(int[] arr) {  
        int n = arr.length;  
        for (int i = 1; i < n; i++) {  
            int key = arr[i]; // 要插入的元素  
            int j = i - 1;  
  
            // 將比key大的元素向後移動  
            while (j >= 0 && arr[j] > key) {  
                arr[j + 1] = arr[j];  
                j = j - 1;  
            }  
            arr[j + 1] = key; // 將key插入到正確的位置  
        }  
    }  
}

4. 快速排序(Quick Sort)

基本思想:透過一趟排序將待排記錄分割為獨立的兩個部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,然後對這兩部分記錄繼續進行排序,以達到整個序列有序。
特點:不穩定排序,但效率較高,時間複雜度為O(nlogn),空間複雜度為O(logn)。

示例程式碼

public class QuickSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用快速排序對陣列進行排序  
        quickSort(arr, 0, arr.length - 1);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 快速排序方法  
    public static void quickSort(int[] arr, int low, int high) {  
        if (low < high) {  
            // pi是分割槽索引,arr[p]現在已經到位  
            int pi = partition(arr, low, high);  
  
            // 分別對分割槽前後部分進行排序  
            quickSort(arr, low, pi - 1);  
            quickSort(arr, pi + 1, high);  
        }  
    }  
  
    // 分割槽操作  
    public static int partition(int[] arr, int low, int high) {  
        int pivot = arr[high];    // 選擇最右邊的元素作為軸點  
        int i = (low - 1);  // 最小元素索引  
  
        for (int j = low; j <= high - 1; j++) {  
            // 如果當前元素小於或等於軸點  
            if (arr[j] <= pivot) {  
                i++;    // 遞增索引  
                // 交換 arr[i] 和 arr[j]  
                int temp = arr[i];  
                arr[i] = arr[j];  
                arr[j] = temp;  
            }  
        }  
  
        // 將軸點元素放到最終的位置  
        int temp = arr[i + 1];  
        arr[i + 1] = arr[high];  
        arr[high] = temp;  
  
        return i + 1;  
    }  
}

5. 歸併排序(Merge Sort)

基本思想:將兩個或兩個以上的有序表合併成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的,然後再把有序子序列合併為整體有序序列。
特點:穩定排序,時間複雜度為O(nlogn),但空間複雜度較高,為O(n)。

public class MergeSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用歸併排序對陣列進行排序  
        arr = mergeSort(arr, 0, arr.length - 1);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 歸併排序方法  
    public static int[] mergeSort(int[] arr, int left, int right) {  
        if (left < right) {  
            // 找到中間索引  
            int mid = left + (right - left) / 2;  
  
            // 對左半部分進行歸併排序  
            arr = mergeSort(arr, left, mid);  
  
            // 對右半部分進行歸併排序  
            arr = mergeSort(arr, mid + 1, right);  
  
            // 合併兩個已排序的部分  
            merge(arr, left, mid, right);  
        }  
        return arr;  
    }  
  
    // 合併兩個已排序的部分  
    public static void merge(int[] arr, int left, int mid, int right) {  
        int n1 = mid - left + 1;  
        int n2 = right - mid;  
  
        // 建立臨時陣列  
        int[] L = new int[n1];  
        int[] R = new int[n2];  
  
        // 複製資料到臨時陣列  
        for (int i = 0; i < n1; ++i)  
            L[i] = arr[left + i];  
        for (int j = 0; j < n2; ++j)  
            R[j] = arr[mid + 1 + j];  
  
        // 合併臨時陣列到arr[left..right]  
        int i = 0, j = 0, k = left;  
        while (i < n1 && j < n2) {  
            if (L[i] <= R[j]) {  
                arr[k] = L[i];  
                i++;  
            } else {  
                arr[k] = R[j];  
                j++;  
            }  
            k++;  
        }  
  
        // 複製L[]的剩餘元素  
        while (i < n1) {  
            arr[k] = L[i];  
            i++;  
            k++;  
        }  
  
        // 複製R[]的剩餘元素  
        while (j < n2) {  
            arr[k] = R[j];  
            j++;  
            k++;  
        }  
    }  
}

6. 堆排序(Heap Sort)

基本思想:利用堆這種資料結構的特性,首先將待排序的資料構建成一個最大(或最小)堆,然後將堆頂元素與最後一個元素交換,並重新調整堆,這樣就得到了最大(或最小)值。重複這個過程直到所有資料元素有序。
特點:不穩定排序,但效率較高,時間複雜度為O(nlogn),空間複雜度為O(1)。

public class HeapSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用堆排序對陣列進行排序  
        heapSort(arr);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 堆排序方法  
    public static void heapSort(int[] arr) {  
        int n = arr.length;  
  
        // 構建最大堆  
        for (int i = n / 2 - 1; i >= 0; i--) {  
            heapify(arr, n, i);  
        }  
  
        // 一個個從堆頂取出元素  
        for (int i = n - 1; i >= 0; i--) {  
            // 將當前最大的元素arr[0]和arr[i]交換  
            int temp = arr[0];  
            arr[0] = arr[i];  
            arr[i] = temp;  
  
            // 重新對剩下的元素進行堆化  
            heapify(arr, i, 0);  
        }  
    }  
  
    // 堆化方法  
    public static void heapify(int[] arr, int n, int i) {  
        int largest = i; // 初始化最大值為根  
        int left = 2 * i + 1; // 左子節點  
        int right = 2 * i + 2; // 右子節點  
  
        // 如果左子節點比根大  
        if (left < n && arr[left] > arr[largest]) {  
            largest = left;  
        }  
  
        // 如果右子節點比當前最大的還大  
        if (right < n && arr[right] > arr[largest]) {  
            largest = right;  
        }  
  
        // 如果最大元素不是根  
        if (largest != i) {  
            // 交換  
            int swap = arr[i];  
            arr[i] = arr[largest];  
            arr[largest] = swap;  
  
            // 遞迴堆化受影響的子樹  
            heapify(arr, n, largest);  
        }  
    }  
}

7. 希爾排序(Shell Sort)

基本思想:先將整個待排記錄序列分割為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。

例項程式碼

public class ShellSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {64, 34, 25, 12, 22, 11, 90};  
  
        // 使用希爾排序對陣列進行排序  
        shellSort(arr);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 希爾排序方法  
    public static void shellSort(int[] arr) {  
        int n = arr.length;  
        int gap = n / 2; // 初始間隔  
  
        // 動態定義間隔序列  
        while (gap > 0) {  
            for (int i = gap; i < n; i++) {  
                int temp = arr[i];  
                int j;  
  
                // 對每個子序列進行插入排序  
                for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {  
                    arr[j] = arr[j - gap];  
                }  
                arr[j] = temp;  
            }  
  
            // 更新間隔  
            gap /= 2;  
        }  
    }  
}

特點:不穩定排序,是插入排序的一種更高效的改進版本,時間複雜度依賴於增量序列的選擇。

8. 基數排序(Radix Sort)

基本思想:將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位開始,依次進行一次排序。
特點:穩定排序,適用於整數排序,時間複雜度取決於整數的位數和採用的基數。

示例程式碼

public class RadixSortExample {  
    public static void main(String[] args) {  
        // 示例陣列  
        int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};  
  
        // 使用基數排序對陣列進行排序  
        radixSort(arr);  
  
        // 輸出排序後的陣列  
        for (int num : arr) {  
            System.out.print(num + " ");  
        }  
    }  
  
    // 基數排序方法  
    public static void radixSort(int[] arr) {  
        // 找到陣列中的最大數  
        int max = findMax(arr);  
  
        // 獲取最大數的位數  
        int maxDigit = getMaxDigit(max);  
  
        // 初始化臨時陣列  
        int[][] temp = new int[10][arr.length];  
        int[] count = new int[10]; // 用於計數  
  
        // 對每一位進行排序  
        for (int i = 1; i <= maxDigit; i++) {  
            // 將陣列元素分配到臨時陣列  
            for (int j = 0; j < arr.length; j++) {  
                int digit = getDigit(arr[j], i);  
                temp[digit][count[digit]] = arr[j];  
                count[digit]++;  
            }  
  
            // 將臨時陣列中的元素複製回原陣列  
            int index = 0;  
            for (int k = 0; k < 10; k++) {  
                if (count[k] != 0) {  
                    for (int l = 0; l < count[k]; l++) {  
                        arr[index++] = temp[k][l];  
                    }  
                    count[k] = 0; // 重置計數  
                }  
            }  
        }  
    }  
  
    // 找到陣列中的最大數  
    private static int findMax(int[] arr) {  
        int max = arr[0];  
        for (int i = 1; i < arr.length; i++) {  
            if (arr[i] > max) {  
                max = arr[i];  
            }  
        }  
        return max;  
    }  
  
    // 獲取最大數的位數  
    private static int getMaxDigit(int num) {  
        int count = 0;  
        while (num != 0) {  
            num /= 10;  
            count++;  
        }  
        return count;  
    }  
  
    // 獲取一個數的第i位(從最低位開始計數)  
    private static int getDigit(int num, int i) {  
        return (num / (int) Math.pow(10, i - 1)) % 10;  
    }  
}

相關文章