public class QuickSortBasic {
public static void sort(int[] a) { sort(a, 0, a.length - 1); }
private static void sort(int[] a, int lo, int hi) {
if (lo >= hi) return;
int p = partition(a, lo, hi);
sort(a, lo, p - 1);
sort(a, p + 1, hi);
}
private static int partition(int[] a, int lo, int hi) {
int pivot = a[hi]; // 選最後元素
int i = lo - 1;
for (int j = lo; j < hi; j++) {
if (a[j] <= pivot) swap(a, ++i, j);
}
swap(a, i + 1, hi);
return i + 1;
}
private static void swap(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; }
}
private static final int INSERTION_SORT_THRESHOLD = 15;
private static void sort(int[] a, int lo, int hi) {
if (hi - lo <= INSERTION_SORT_THRESHOLD) {
insertionSort(a, lo, hi);
return;
}
// ... 原快排邏輯
}
private static void insertionSort(int[] a, int lo, int hi) {
for (int i = lo + 1; i <= hi; i++) {
int key = a[i];
int j = i - 1;
while (j >= lo && a[j] > key) a[j + 1] = a[j--];
a[j + 1] = key;
}
}
private static int medianOfThree(int[] a, int lo, int hi) {
int mid = lo + (hi - lo) / 2;
// 排序 lo, mid, hi 三者,中位數放到 hi-1
if (a[lo] > a[mid]) swap(a, lo, mid);
if (a[lo] > a[hi]) swap(a, lo, hi);
if (a[mid] > a[hi]) swap(a, mid, hi);
swap(a, mid, hi - 1); // 基準值移至 hi-1
return a[hi - 1];
}
// 分區時調用
int pivot = medianOfThree(a, lo, hi);
// 掃描範圍縮為 [lo+1, hi-2]
private static void sort3Way(int[] a, int lo, int hi) {
if (lo >= hi) return;
int lt = lo, i = lo + 1, gt = hi;
int v = a[lo]; // 基準值
while (i <= gt) {
if (a[i] < v) swap(a, lt++, i++);
else if (a[i] > v) swap(a, i, gt--);
else i++;
}
sort3Way(a, lo, lt - 1); // 遞歸 < 區
sort3Way(a, gt + 1, hi); // 遞歸 > 區
// = 區已有序,無需遞歸
}
效果:重複鍵多時從 O(n²) 降為 O(n),接近線性。
public static void sortIterative(int[] a) {
// 手動棧模擬遞歸,先處理小分區,限制棧深度 O(log n)
int[] stack = new int[64]; // 足夠大 (2^64 元素)
int top = -1;
stack[++top] = 0;
stack[++top] = a.length - 1;
while (top >= 0) {
int hi = stack[top--];
int lo = stack[top--];
if (lo >= hi) continue;
if (hi - lo <= INSERTION_SORT_THRESHOLD) {
insertionSort(a, lo, hi);
continue;
}
int p = partition3Way(a, lo, hi); // 返回 lt, gt 需封裝對象或雙值
// 先壓入大分區,再壓入小分區 → 小分區優先處理 → 棧深度 O(log n)
if (p.gt - p.gt > p.lt - lo) { // 簡化判斷
push(stack, lo, p.lt - 1);
push(stack, p.gt + 1, hi);
} else {
push(stack, p.gt + 1, hi);
push(stack, lo, p.lt - 1);
}
}
}
private static void push(int[] stack, int lo, int hi) {
if (lo < hi) { stack[++top] = lo; stack[++top] = hi; }
}
private static final Random RAND = new Random();
private static int randomPartition(int[] a, int lo, int hi) {
int randIdx = lo + RAND.nextInt(hi - lo + 1);
swap(a, randIdx, hi); // 隨機元素換到末尾
return partition(a, lo, hi);
}
public class QuickSortPro {
private static final int INSERTION_THRESHOLD = 15;
private static final Random RAND = new Random();
public static void sort(int[] a) {
if (a == null || a.length <= 1) return;
shuffle(a); // 全局隨機化,消除輸入相關性
sort(a, 0, a.length - 1);
}
private static void sort(int[] a, int lo, int hi) {
while (lo < hi) { // 尾遞歸消除:大分區用循環,小分區遞歸
if (hi - lo <= INSERTION_THRESHOLD) {
insertionSort(a, lo, hi);
return;
}
int[] bounds = partition3Way(a, lo, hi); // [lt, gt]
// 先處理小分區 (遞歸),大分區留給 while 循環 (尾遞歸消除)
if (bounds[0] - lo < hi - bounds[1]) {
sort(a, lo, bounds[0] - 1);
lo = bounds[1] + 1; // 尾遞歸消除:更新 lo 繼續循環
} else {
sort(a, bounds[1] + 1, hi);
hi = bounds[0] - 1;
}
}
}
// 三向切分返回 [lt, gt]
private static int[] partition3Way(int[] a, int lo, int hi) {
int v = a[lo];
int lt = lo, i = lo + 1, gt = hi;
while (i <= gt) {
if (a[i] < v) swap(a, lt++, i++);
else if (a[i] > v) swap(a, i, gt--);
else i++;
}
return new int[]{lt, gt};
}
private static void shuffle(int[] a) {
for (int i = a.length - 1; i > 0; i--) swap(a, i, RAND.nextInt(i + 1));
}
// insertionSort, swap 同前
}
| 版本 | 耗時 (ms) | 特點 |
|---|---|---|
| 基礎快排 (固定 pivot) | ~450 | 有序數組極慢 |
| 三數取中 | ~320 | 通用改善 |
| 隨機化 + 三向切分 | ~210 | 重複鍵極快 |
| 完整生產級 (尾遞歸消除+插入排序) | ~180 | 最穩健 |
Arrays.sort() (JDK 17) | ~150 | 雙軸快排 + 內部優化 |