排序演算法是資料結構基石。本文以 Java 實作三種經典排序:快速排序 (分治、平均 O(n log n))、氣泡排序 (教學用、O(n²))、選擇排序 (原地、O(n²)),並對比 Arrays.sort() (Dual-Pivot QuickSort + TimSort) 的工業級實作,幫助理解原理與效能差距。
public class QuickSort {
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) return;
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int left, int right) {
if (left >= right) return;
int pivotIndex = partition(arr, left, right);
quickSort(arr, left, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, right);
}
private static int partition(int[] arr, int left, int right) {
// 隨機基準值避免最壞情況
int rand = left + new Random().nextInt(right - left + 1);
swap(arr, rand, right);
int pivot = arr[right];
int i = left - 1;
for (int j = left; j < right; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, right);
return i + 1;
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
}
重複遍歷陣列,相鄰元素比較交換,大值像氣泡浮到右端。
public class BubbleSort {
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) return;
boolean swapped;
int start = 0, end = arr.length - 1;
do {
swapped = false;
// 正向:大值右移
for (int i = start; i < end; i++) {
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
swapped = true;
}
}
if (!swapped) break;
end--;
// 反向:小值左移
for (int i = end; i > start; i--) {
if (arr[i] < arr[i - 1]) {
swap(arr, i, i - 1);
swapped = true;
}
}
start++;
} while (swapped);
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
}
每輪從未排序區選最小值,換到已排序區末尾。
public class SelectionSort {
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) return;
for (int i = 0; i < arr.length - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIdx]) minIdx = j;
}
if (minIdx != i) swap(arr, i, minIdx);
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
}
| 資料類型 | 演算法 | 複雜度 | 穩定性 |
|---|---|---|---|
| 基本型別 (int[], long[]...) | Dual-Pivot QuickSort (Yaroslavskiy) | O(n log n) | 不穩定 |
| 物件陣列 (Object[]) | TimSort (合併排序 + 插入排序) | O(n log n) | 穩定 |
| 平行排序 (parallelSort) | ForkJoin + 上述演算法 | O(n log n / P) | 視基礎演算法 |
關鍵優化:小陣列 (< 47) 改用插入排序;偵測有序段落 (run) 直接合併;雙基準值減少遞迴深度。
| 方法 | 耗時 (ms) | 備註 |
|---|---|---|
| Arrays.sort() | ~120 | 基準 |
| Arrays.parallelSort() | ~45 | 8 核心 CPU |
| 快速排序 (上方實作) | ~180 | 單執行緒、無內建優化 |
| 氣泡排序 | ~45,000 | 極慢,僅供教學 |
| 選擇排序 | ~38,000 | 極慢 |
Arrays.sort() / Collections.sort() / Stream.sorted()。