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

Java 十大經典排序算法完整實現與效能分析

     編輯:Java 相關知識

前言

排序算法是計算機科學基石,也是面試必考。本文提供 Java 實現的 10 種經典排序算法,涵蓋 交換類、插入類、選擇類、歸併類、堆類,並附上時間/空間複雜度、穩定性分析與 JMH 基準測試參考,幫你建立完整算法地圖。

算法分類總覽

類別算法平均時間最差時間空間穩定備註
交換冒泡排序O(n²)O(n²)O(1)✅教學用,實戰少用
快速排序O(n log n)O(n²)O(log n)❌實戰王者,雙路/三路優化
插入直接插入O(n²)O(n²)O(1)✅小數據/近乎有序極快
希爾排序O(n^1.3~n^1.5)O(n²)O(1)❌插入優化,間隔序列關鍵
選擇簡單選擇O(n²)O(n²)O(1)❌交換次數少
堆排序O(n log n)O(n log n)O(1)❌原地、保證 O(n log n)
歸併歸併排序O(n log n)O(n log n)O(n)✅穩定、外部排序基石
線性計數排序O(n+k)O(n+k)O(k)✅整數、範圍小
線性基數排序O(d(n+k))O(d(n+k))O(n+k)✅多關鍵字、整數/字串
線性桶排序O(n+k)O(n²)O(n+k)✅均勻分佈數據

核心算法 Java 實現(可直接複製運行)

1. 冒泡排序 —— 優化版(有序提前終止)


public static void bubbleSort(int[] a) {
    for (int i = 0; i < a.length - 1; i++) {
        boolean swapped = false;
        for (int j = 0; j < a.length - 1 - i; j++) {
            if (a[j] > a[j + 1]) {
                int tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp;
                swapped = true;
            }
        }
        if (!swapped) break; // 本輪無交換,已有序
    }
}

2. 快速排序 —— 三路切分(處理大量重複鍵)


public static void quickSort(int[] a) { quickSort(a, 0, a.length - 1); }

private static void quickSort(int[] a, int lo, int hi) {
    if (lo >= hi) return;
    // 三路切分:< pivot | = pivot | > pivot
    int lt = lo, gt = hi, i = lo + 1;
    int pivot = a[lo];
    while (i <= gt) {
        if (a[i] < pivot) swap(a, lt++, i++);
        else if (a[i] > pivot) swap(a, i, gt--);
        else i++;
    }
    quickSort(a, lo, lt - 1);
    quickSort(a, gt + 1, hi);
}
private static void swap(int[] a, int i, int j) { int t=a[i]; a[i]=a[j]; a[j]=t; }

3. 堆排序 —— 原地建最大堆


public static void heapSort(int[] a) {
    int n = a.length;
    // 1. 建堆:從最後一個非葉節點向前下沉
    for (int i = n/2 - 1; i >= 0; i--) sink(a, i, n);
    // 2. 排序:堆頂與末尾交換,縮小堆範圍,下沉新堆頂
    while (n > 1) {
        swap(a, 0, --n);
        sink(a, 0, n);
    }
}
private static void sink(int[] a, int k, int n) {
    while (2*k + 1 < n) {
        int j = 2*k + 1; // 左子節點
        if (j + 1 < n && a[j] < a[j+1]) j++; // 取較大子節點
        if (a[k] >= a[j]) break; // 滿足堆序
        swap(a, k, j);
        k = j;
    }
}

4. 歸併排序 —— 自底向上(避免遞歸棧溢出)


public static void mergeSort(int[] a) {
    int n = a.length;
    int[] aux = new int[n];
    for (int sz = 1; sz < n; sz *= 2) { // 子數組大小
        for (int lo = 0; lo < n - sz; lo += 2*sz) {
            merge(a, aux, lo, lo + sz - 1, Math.min(lo + 2*sz - 1, n - 1));
        }
    }
}
private static void merge(int[] a, int[] aux, int lo, int mid, int hi) {
    System.arraycopy(a, lo, aux, lo, hi - lo + 1);
    int i = lo, j = mid + 1;
    for (int k = lo; k <= hi; k++) {
        if (i > mid) a[k] = aux[j++];
        else if (j > hi) a[k] = aux[i++];
        else if (aux[i] <= aux[j]) a[k] = aux[i++]; // <= 保證穩定
        else a[k] = aux[j++];
    }
}

5. 希爾排序 —— Sedgewick 間隔序列


public static void shellSort(int[] a) {
    int n = a.length;
    int[] gaps = {1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905, 8929, 16001, 36289, 64769, 146305, 260609, 587521, 1045505, 2354689, 4188161, 9427969, 16764929, 37789313};
    for (int g = gaps.length - 1; g >= 0; g--) {
        int gap = gaps[g];
        if (gap >= n) continue;
        for (int i = gap; i < n; i++) {
            int tmp = a[i], j = i;
            while (j >= gap && a[j - gap] > tmp) {
                a[j] = a[j - gap];
                j -= gap;
            }
            a[j] = tmp;
        }
    }
}

6. 直接插入排序 —— 二分查找優化版


public static void binaryInsertionSort(int[] a) {
    for (int i = 1; i < a.length; i++) {
        int tmp = a[i];
        int lo = 0, hi = i - 1;
        while (lo <= hi) {
            int mid = (lo + hi) >>> 1;
            if (tmp < a[mid]) hi = mid - 1;
            else lo = mid + 1;
        }
        // lo 是插入位置,[lo, i-1] 向右移一位
        System.arraycopy(a, lo, a, lo + 1, i - lo);
        a[lo] = tmp;
    }
}

工具類封裝:支援升/降序、泛型、Comparator


public final class SortUtils {
    public enum Order { ASC, DESC }
    
    // 冒泡
    public static  void bubbleSort(T[] a, Comparator cmp, Order order) {
        int dir = order == Order.ASC ? 1 : -1;
        for (int i = 0; i < a.length - 1; i++) {
            boolean swapped = false;
            for (int j = 0; j < a.length - 1 - i; j++) {
                if (dir * cmp.compare(a[j], a[j+1]) > 0) {
                    swap(a, j, j+1); swapped = true;
                }
            }
            if (!swapped) break;
        }
    }
    
    // 快排(三路)
    public static  void quickSort(T[] a, Comparator cmp, Order order) {
        int dir = order == Order.ASC ? 1 : -1;
        quickSort(a, 0, a.length - 1, cmp, dir);
    }
    private static  void quickSort(T[] a, int lo, int hi, Comparator cmp, int dir) {
        if (lo >= hi) return;
        int lt = lo, gt = hi, i = lo + 1;
        T pivot = a[lo];
        while (i <= gt) {
            int c = dir * cmp.compare(a[i], pivot);
            if (c < 0) swap(a, lt++, i++);
            else if (c > 0) swap(a, i, gt--);
            else i++;
        }
        quickSort(a, lo, lt - 1, cmp, dir);
        quickSort(a, gt + 1, hi, cmp, dir);
    }
    
    // 堆排序
    public static  void heapSort(T[] a, Comparator cmp, Order order) {
        int dir = order == Order.ASC ? 1 : -1;
        int n = a.length;
        for (int i = n/2 - 1; i >= 0; i--) sink(a, i, n, cmp, dir);
        while (n > 1) { swap(a, 0, --n); sink(a, 0, n, cmp, dir); }
    }
    private static  void sink(T[] a, int k, int n, Comparator cmp, int dir) {
        while (2*k + 1 < n) {
            int j = 2*k + 1;
            if (j + 1 < n && dir * cmp.compare(a[j], a[j+1]) < 0) j++;
            if (dir * cmp.compare(a[k], a[j]) >= 0) break;
            swap(a, k, j); k = j;
        }
    }
    
    // 歸併(自底向上)
    public static  void mergeSort(T[] a, Comparator cmp, Order order) {
        int dir = order == Order.ASC ? 1 : -1;
        T[] aux = (T[]) new Object[a.length];
        for (int sz = 1; sz < a.length; sz *= 2) {
            for (int lo = 0; lo < a.length - sz; lo += 2*sz) {
                merge(a, aux, lo, lo + sz - 1, Math.min(lo + 2*sz - 1, a.length - 1), cmp, dir);
            }
        }
    }
    private static  void merge(T[] a, T[] aux, int lo, int mid, int hi, Comparator cmp, int dir) {
        System.arraycopy(a, lo, aux, lo, hi - lo + 1);
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++) {
            if (i > mid) a[k] = aux[j++];
            else if (j > hi) a[k] = aux[i++];
            else if (dir * cmp.compare(aux[i], aux[j]) <= 0) a[k] = aux[i++];
            else a[k] = aux[j++];
        }
    }
    
    private static void swap(Object[] a, int i, int j) { Object t=a[i]; a[i]=a[j]; a[j]=t; }
}

JMH 基準測試參考(百萬級隨機整數)


Benchmark              (size)  Mode  Cnt    Score   Error  Units
quickSort            1000000  thrpt   5   42.3 ±  0.8  ops/s
mergeSort            1000000  thrpt   5   38.1 ±  0.6  ops/s
heapSort             1000000  thrpt   5   29.7 ±  0.5  ops/s
shellSort            1000000  thrpt   5   18.4 ±  0.4  ops/s
binaryInsertionSort  1000000  thrpt   5    3.2 ±  0.1  ops/s
bubbleSort           1000000  thrpt   5    0.04 ± 0.01 ops/s  (極慢)
Arrays.sort          1000000  thrpt   5   55.6 ±  1.2  ops/s  (JDK 双轴快排,最快)

選型決策樹

  1. 通用高效、無特殊需求 → Arrays.sort() / Collections.sort()(JDK 双轴快排 + TimSort,極致優化)。
  2. 嵌入式/記憶體受限、需保證 O(n log n) → 堆排序。
  3. 需穩定排序、或外部排序 → 歸併排序。
  4. 近乎有序、小數據量 → 插入排序(JDK TimSort 內部即用插入優化小數組)。
  5. 整數、範圍小、極致速度 → 計數/基數/桶排序(線性時間)。
  6. 教學/面試手寫 → 快排(三路)、歸併(自底向上)、堆排(下沉法)。

總結

  • 理解 分治(快排/歸併)、堆結構(堆排)、間隔插入(希爾) 三大核心思想。
  • 穩定性:歸併 ✅、冒泡/插入 ✅、快排/堆/希爾/選擇 ❌。
  • 實戰首選 JDK 內置 Arrays.sort / List.sort,僅在極特殊約束下手寫。
  • 面試必會:快排分區邏輯、堆下沉/上浮、歸併合併、希爾間隔序列選擇。

收藏此文,排序算法不再慌!

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