排序是程序設計基石。JDK 7 引入 DualPivotQuickSort 替代傳統單軸快排,顯著提升性能。本文深度解析其核心實現:雙軸快排分區邏輯、插入排序優化(傳統版與成對插入版),並探討優化背後的工程思維。
sort(int[] a, int left, int right, boolean leftmost)left、right:待排序區間索引(含邊界)leftmost:是否為數組最左區間(影響插入排序策略)leftmost == true)for (int i = left, j = i; i < right; j = ++i) {
int ai = a[i + 1];
while (ai < a[j]) {
a[j + 1] = a[j];
if (j-- == left) break;
}
a[j + 1] = ai;
}
類似撲克牌插牌,每次將 a[i+1] 插入已排序的 a[left..i] 中。
leftmost == false)—— 創新點for (int k = left; ++left <= right; k = ++left) {
int a1 = a[k], a2 = a[left];
if (a1 < a2) { int t = a1; a1 = a2; a2 = t; } // 確保 a1 >= a2
// 插入較大者 a1
while (a1 < a[--k]) a[k + 2] = a[k];
a[++k + 1] = a1;
// 插入較小者 a2
while (a2 < a[--k]) a[k + 1] = a[k];
a[k + 1] = a2;
}
最左區間 leftmost=true 時,left=0。成對版內層 --k 可能變為 -1 導致越界,需額外邊界檢查反而拖慢性能。傳統版自然處理邊界(j-- == left),更安全高效。
pivot1 <= pivot2)。a[left] = pivot1, a[right] = pivot2。左部分 中間部分 右部分
< pivot1 pivot1 <= x <= pivot2 > pivot2
[left, less) [less, k) [k, great] (great, right]
less:左部分邊界(初始 left+1)great:右部分邊界(初始 right-1)k:掃描指針(從 less 向 great 掃描)while (k <= great) {
if (a[k] < pivot1) { // 情況 A:屬於左部分
swap(a, k++, less++);
} else if (a[k] > pivot2) { // 情況 B:屬於右部分
while (a[great] > pivot2) great--;
if (a[great] < pivot1) { // B1:實際屬於左部分
swap(a, k, less);
swap(a, k, great--);
less++;
} else { // B2:屬於右部分
swap(a, k, great--);
}
} else { // 情況 C:屬於中間部分
k++;
}
}
// 最後將兩個樞軸歸位
swap(a, left, --less);
swap(a, right, ++great);
sort(a, left, less - 1, false); // < pivot1
sort(a, less + 1, great - 1, false); // pivot1..pivot2
sort(a, great + 1, right, false); // > pivot2
| 維度 | 單軸快排 | 雙軸快排 |
|---|---|---|
| 分區數 | 2 段(<p, >=p) | 3 段(<p1, p1..p2, >p2) |
| 遞歸深度 | 較深 | 淺(中間段不再遞歸或極短) |
| 交換次數 | 較多 | 少(元素更快歸位) |
| 緩存友好 | 一般 | 優(掃描方向單一,預取效果好) |
leftmost 區分邊界條件,避免越界檢查開銷。JDK 7 DualPivotQuickSort 是算法工程化的教科書級範例:在理論最優(單軸)與工程實際(雙軸、成對插入、五等分取樣)之間找到平衡。閱讀其源碼,不僅學到排序,更學到如何在約束下做最優工程決策。