首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 經典排序算法大全:原理、實現、效能測試與選型指南

     編輯:Java 相關知識

前言:為什麼還要手寫排序?

雖然生產環境直接用 Arrays.sort()/Collections.sort()(TimSort,O(n log n) 穩定),但理解經典算法是面試必考、演算法思維訓練、特定場景(如外部排序、Top-K、近似有序資料)優化的基石。本文整理 14 種常見排序,附完整 Java 實現、原理圖解、效能實測(20,000 筆隨機整數)與選型建議。

一、O(n²) 基礎排序(教學/小規模/近似有序)

1. 冒泡排序

public static void bubbleSort(int[] a) {
    for (int i = 0; i < a.length; i++)
        for (int j = 0; j < a.length - i - 1; j++)
            if (a[j] > a[j+1]) swap(a, j, j+1);
}
  • 特點:穩定、原地、最差/平均 O(n²),最佳 O(n)(已有序)。

2. 改良冒泡(旗標提前結束)

public static void bubbleSortAdv(int[] a) {
    boolean swapped = true;
    for (int limit = a.length - 1; swapped && limit > 0; limit--) {
        swapped = false;
        for (int i = 0; i < limit; i++)
            if (a[i] > a[i+1]) { swap(a, i, i+1); swapped = true; }
    }
}

3. 改良冒泡 II(記錄最後交換位置)

public static void bubbleSortAdv2(int[] a) {
    int limit = a.length - 1;
    while (limit > 0) {
        int lastSwap = 0;
        for (int i = 0; i < limit; i++)
            if (a[i] > a[i+1]) { swap(a, i, i+1); lastSwap = i+1; }
        limit = lastSwap;
    }
}

4. 選擇排序

public static void selectSort(int[] a) {
    for (int i = 0; i < a.length; i++) {
        int min = i;
        for (int j = i+1; j < a.length; j++)
            if (a[j] < a[min]) min = j;
        swap(a, i, min);
    }
}
  • 特點:不穩定、原地、交換次數最少 (n-1),適合寫入成本高的存儲介質。

5. 插入排序

public static void insertSort(int[] a) {
    for (int i = 1; i < a.length; i++) {
        int tmp = a[i], j = i-1;
        while (j >= 0 && a[j] > tmp) a[j+1] = a[j--];
        a[j+1] = tmp;
    }
}
  • 特點:穩定、原地、近似有序資料 O(n)、線上算法(可邊讀邊排)。

6. 改良插入(二分查找插入位置)

public static void insertSortBin(int[] a) {
    for (int i = 1; i < a.length; i++) {
        int tmp = a[i], pos = Arrays.binarySearch(a, 0, i, tmp);
        if (pos < 0) pos = -pos - 1;
        System.arraycopy(a, pos, a, pos+1, i-pos);
        a[pos] = tmp;
    }
}

二、O(n log n) 高效排序(生產級主力)

7. 希爾排序(插入排序改良,縮小增量)

public static void shellSort(int[] a) {
    for (int gap = a.length/2; gap > 0; gap /= 2)
        for (int i = gap; i < a.length; i++) {
            int tmp = a[i], j = i;
            while (j >= gap && a[j-gap] > tmp) { a[j] = a[j-gap]; j -= gap; }
            a[j] = tmp;
        }
}
  • 特點:不穩定、原地、增量序列影響巨大(Hibbard、Sedgewick、Ciura 序列可達 O(n^1.25))。

8. 歸併排序

public static int[] mergeSort(int[] a) {
    if (a.length <= 1) return a;
    int mid = a.length/2;
    int[] left = mergeSort(Arrays.copyOfRange(a, 0, mid));
    int[] right = mergeSort(Arrays.copyOfRange(a, mid, a.length));
    return merge(left, right);
}
private static int[] merge(int[] a, int[] b) {
    int[] r = new int[a.length + b.length];
    int i=0,j=0,k=0;
    while (i
  • 特點:穩定、非原地 (O(n) 空間)、保證 O(n log n)、適合外部排序/串流處理。

9. 快速排序(雙向掃描版,易理解)

public static void quickSort(int[] a, int lo, int hi) {
    if (lo >= hi) return;
    int i = lo, j = hi, pivot = a[lo];
    while (i < j) {
        while (i < j && a[j] >= pivot) j--;
        if (i < j) a[i++] = a[j];
        while (i < j && a[i] <= pivot) i++;
        if (i < j) a[j--] = a[i];
    }
    a[i] = pivot;
    quickSort(a, lo, i-1);
    quickSort(a, i+1, hi);
}
public static void quickSort(int[] a) { quickSort(a, 0, a.length-1); }
  • 特點:不穩定、原地、平均 O(n log n)、最差 O(n²)(可用隨機 pivot/三數取中避免)、快取友好、實際最快。

10. 堆排序

public static void heapSort(int[] a) {
    for (int i = (a.length-2)/2; i >= 0; i--) maxHeapify(a, i, a.length);
    for (int i = a.length-1; i > 0; i--) {
        swap(a, 0, i);
        maxHeapify(a, 0, i);
    }
}
private static void maxHeapify(int[] a, int i, int len) {
    int tmp = a[i];
    for (int j = 2*i+1; j < len; j = 2*j+1) {
        if (j+1 < len && a[j+1] > a[j]) j++;
        if (a[j] < tmp) break;
        a[i] = a[j]; i = j;
    }
    a[i] = tmp;
}
  • 特點:不穩定、原地、保證 O(n log n)、適合即時系統/記憶體受限。

三、特殊排序

11. 二叉樹排序(BST)

建構 BST 再中序遍歷。平均 O(n log n),最差 O(n²)(退化鏈表),需自平衡樹(AVL/紅黑)保證效能,教學用途為主。

四、效能實測報告(20,000 隨機整數,平均 20 次)

算法耗時 (ms)複雜度穩定性備註
bubbleSort766O(n²)✅教學用
bubbleSortAdv662O(n²)✅旗標優化
bubbleSortAdv2647O(n²)✅記錄最後交換
selectSort252O(n²)❌交換少
insertSort218O(n²)✅近似有序極快
insertSortBin127O(n²)✅二分查找位置
shellSort2~O(n^1.3)❌增量序列關鍵
mergeSort3O(n log n)✅穩定、需額外空間
quickSort1O(n log n)❌實際最快
heapSort2O(n log n)❌保證上界

五、選型決策樹

  1. 資料量 < 50 / 近似有序 → 插入排序 / 希爾排序
  2. 需穩定排序 → 歸併排序 / TimSort (Arrays.sort 物件版)
  3. 一般通用、追求極致速度 → 快速排序(雙軸/三向切分) / Arrays.sort 基本型版(雙軸快排)
  4. 記憶體極度受限 / 硬性實時要求 → 堆排序
  5. 外部排序 / 串流 / 鏈表 → 歸併排序
  6. 生產環境 → 永遠優先用 JDK 內建 Arrays.sort/Collections.sort,經過極致優化(TimSort/雙軸快排)、穩定、無 Bug。

六、結語

排序算法是演算法皇冠上的明珠。從冒泡的樸素到快排的優雅,從歸併的穩健到堆排的剛性,每種算法都折射出「分治」、「隨機化」、「資料結構輔助」等核心思想。掌握它們的適用邊界,比背誦程式碼更有價值。下次面試被問到「為什麼快排通常比歸併快?」時,你可以自信地回答:原地分區、快取友好、分支預測友好、避免額外記憶體分配與拷貝。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved