排序演算法是演算法入門的必修課。本文以插入排序為主,輔以冒泡排序對比,通過代碼實現與圖解剖析其核心邏輯、時間複雜度與適用場景。
將數組分為已排序區間與未排序區間。初始時已排序區間僅含首元素。每輪從未排序區間取出首個元素,在已排序區間中從右向左比較,找到插入位置並將較大元素右移,最後插入該元素。重複直到未排序區間為空。
public class InsertSort {
public InsertSort(int[] a) {
int length = a.length;
// 外層循環:遍歷無序區間 [1, length-1]
for (int j = 1; j <= length - 1; j++) {
int i = j - 1; // 有序區間末尾索引
int temp = a[j]; // 待插入元素(備份,避免覆蓋)
// 內層循環:在有序區間 [0, i] 中從右向左尋找插入位置
while (i >= 0 && a[i] > temp) {
a[i + 1] = a[i]; // 較大元素右移
i--;
}
a[i + 1] = temp; // 插入空位
}
}
public static void main(String[] args) {
int[] a = {6, 8, 9, 43, 89, 5, 7, 3};
new InsertSort(a);
for (int n : a) {
System.out.println(n);
}
}
}
i >= 0:防止索引越界,原代碼用 if (i < 0) break; 等效但不如循環條件直觀。temp:因右移操作會覆蓋原位置值,必須先保存待插入元素。{6, 8, 9, 43, 89, 5, 7, 3} 為例)| 輪次 | 待插入元素 | 有序區間變化 | 數組狀態 |
|---|---|---|---|
| 1 | 8 | {6} → {6, 8} | {6, 8, 9, 43, 89, 5, 7, 3} |
| 2 | 9 | {6, 8} → {6, 8, 9} | {6, 8, 9, 43, 89, 5, 7, 3} |
| 3 | 43 | {6, 8, 9} → {6, 8, 9, 43} | {6, 8, 9, 43, 89, 5, 7, 3} |
| 4 | 89 | {6, 8, 9, 43} → {6, 8, 9, 43, 89} | {6, 8, 9, 43, 89, 5, 7, 3} |
| 5 | 5 | {6, 8, 9, 43, 89} → {5, 6, 8, 9, 43, 89} | {5, 6, 8, 9, 43, 89, 7, 3} |
| 6 | 7 | ... → {5, 6, 7, 8, 9, 43, 89} | {5, 6, 7, 8, 9, 43, 89, 3} |
| 7 | 3 | ... → {3, 5, 6, 7, 8, 9, 43, 89} | {3, 5, 6, 7, 8, 9, 43, 89} |
重複遍歷數組,比較相鄰元素,若逆序則交換。每輪最大元素會「沉底」到末尾,或視角轉換:最小元素像氣泡一樣「冒」到開頭。雙層循環,外層控制輪數,內層做比較交換。
public class BubbleSort {
public static void sort(int[] a) {
int n = a.length;
boolean swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
// 每輪將最大元素推到 n-1-i 位置
for (int j = 0; j < n - 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; // 本輪無交換,說明已有序,提前結束
}
}
}
| 維度 | 插入排序 | 冒泡排序 |
|---|---|---|
| 時間複雜度 (最好) | O(n) — 已有序 | O(n) — 已有序 (優化版) |
| 時間複雜度 (最壞/平均) | O(n²) | O(n²) |
| 空間複雜度 | O(1) | O(1) |
| 穩定性 | 穩定 | 穩定 |
| 數據移動次數 | 少 (僅右移) | 多 (頻繁交換) |
| 適合場景 | 小規模、近乎有序數據 | 教學演示、極小數據集 |
Arrays.sort() (雙軸快排) 或 Collections.sort() (TimSort),不可手寫 O(n²) 排序。插入排序以「人類直覺整理撲克牌」的邏輯實現了原地穩定排序,代碼簡潔且對近乎有序數據極其敏感。冒泡排序雖同屬 O(n²) 但交換開銷大,工程上極少直接使用。理解兩者本質差異,有助於建立演算法分析的基本功——關注數據移動與比較次數的權衡。