排序算法是面試高頻考點,也是算法基石。本文系統梳理 10 大排序算法的核心思想、複雜度、穩定性、適用場景,並提供可直接運行的 Java 實現代碼,助你面試手到擒來。
| 算法 | 平均時間 | 最壞時間 | 空間 | 穩定 | 核心思想 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | ✓ | 相鄰比較交換,小元素冒泡 |
| 選擇排序 | O(n²) | O(n²) | O(1) | ✗ | 每輪選最小元素換到前端 |
| 插入排序 | O(n²) | O(n²) | O(1) | ✓ | 構建有序區,新元素插入合適位置 |
| 希爾排序 | O(n^1.3) | O(n²) | O(1) | ✗ | 分組插入排序,增量逐漸縮小 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | ✗ | 分治+雙指針劃分,基準值左右分離 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | ✗ | 大頂堆選最大換到末尾,篩選調整 |
| 歸並排序 | O(n log n) | O(n log n) | O(n) | ✓ | 分治遞歸,合併兩有序子數組 |
| 計數排序 | O(n+k) | O(n+k) | O(k) | ✓ | 統計頻次,按下標輸出 |
| 桶排序 | O(n+k) | O(n²) | O(n+k) | ✓ | 映射函數分桶,桶內排序再合併 |
| 基數排序 | O(d·n) | O(d·n) | O(n+k) | ✓ | 按位數從低到高分配收集 |
單邊循環法優化版(避免交換開銷):
public static int partition(int[] arr, int left, int right) {
int pivot = arr[left];
while (left < right) {
while (left < right && arr[right] >= pivot) right--;
arr[left] = arr[right]; // 直接覆蓋,省去 swap
while (left < right && arr[left] <= pivot) left++;
arr[right] = arr[left];
}
arr[left] = pivot; // 基準值歸位
return left;
}
關鍵點:基準值選擇影響性能,可用「三數取中」或隨機化避免最壞情況。
public static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right)
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
System.arraycopy(temp, 0, arr, left, temp.length);
}
// 篩選調整:從 start 向下維護大頂堆
public static void heapAdjust(int[] arr, int start, int end) {
int temp = arr[start];
for (int i = 2*start+1; i <= end; i *= 2) {
if (i < end && arr[i] < arr[i+1]) i++; // 取較大子節點
if (temp >= arr[i]) break; // 已滿足堆性質
arr[start] = arr[i]; // 子節點上移
start = i;
}
arr[start] = temp;
}
count[val]++ 統計頻次f(k)=k/10 均勻分布,桶內快排數據量小 (n < 50) → 插入排序 需穩定 + O(n log n) → 歸並排序 一般內部排序 → 快速排序 (隨機化基準) 嚴格 O(n log n) + 原地 → 堆排序 整數小範圍 → 計數排序 浮點數/大範圍均勻分布 → 桶排序 固定位數整數/字符串 → 基數排序
沒有完美的排序,只有最適合場景的排序。面試重點: