直接插入排序是原地、穩定、線性空間的排序演算法。雖然平均時間複雜度為 O(n²),但對小規模資料、近乎有序、或線上排序 (資料逐筆到達) 場景極具優勢。本文提供標準 C++ 實作、效能特性分析與優化變體。
#include <iostream>
#include <vector>
void insertion_sort(std::vector<int>& arr) {
const int n = arr.size();
for (int i = 1; i < n; ++i) {
int key = arr[i]; // 待插入元素
int j = i - 1;
// 從右向左掃描已排序區間,將大於 key 的元素右移
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = key; // 插入正確位置
}
}
// 通用版本:支援任意型別與自訂比較
template <typename RandomIt, typename Compare = std::less<>>
void insertion_sort(RandomIt first, RandomIt last, Compare comp = {}) {
for (auto it = first + 1; it != last; ++it) {
auto key = *it;
auto j = it;
while (j != first && comp(key, *(j - 1))) {
*j = *(j - 1);
--j;
}
*j = key;
}
}
#include <iostream>
#include <vector>
void print_array(const std::vector<int>& arr) {
for (int v : arr) std::cout << v << ' ';
std::cout << '\n';
}
int main() {
std::vector<int> arr = {7, 3, 5, 8, 9, 1, 2, 4, 6};
std::cout << "Before: "; print_array(arr);
insertion_sort(arr);
std::cout << "After: "; print_array(arr);
return 0;
}
/* 輸出:
Before: 7 3 5 8 9 1 2 4 6
After: 1 2 3 4 5 6 7 8 9
*/
| 指標 | 數值 | 說明 |
|---|---|---|
| 最佳時間複雜度 | O(n) | 輸入已排序,內層迴圈零執行 |
| 平均時間複雜度 | O(n²) | 隨機輸入 |
| 最差時間複雜度 | O(n²) | 輸入逆序,每輪需掃描整個已排序區間 |
| 空間複雜度 | O(1) | 原地排序,僅需常數額外空間 |
| 穩定性 | 穩定 | 相等元素相對順序不變 |
| 適應性 | 高 | 近乎有序時極快 |
將線性掃描改為二分搜尋,比較次數從 O(n) 降為 O(log n),但移動次數仍為 O(n²),總體複雜度不變,適合「比較昂貴、移動廉價」的型別。
template <typename RandomIt, typename Compare = std::less<>>
void binary_insertion_sort(RandomIt first, RandomIt last, Compare comp = {}) {
for (auto it = first + 1; it != last; ++it) {
auto key = *it;
// 在 [first, it) 範圍二分搜尋插入點
auto pos = std::lower_bound(first, it, key, comp);
// 將 [pos, it) 右移一位
std::move_backward(pos, it, it + 1);
*pos = key;
}
}
void insertion_sort_sentinel(int* arr, int n) {
for (int i = 1; i < n; ++i) {
if (arr[i] < arr[i-1]) {
int key = arr[i];
int j = i - 1;
arr[0] = key; // 哨兵位置
while (key < arr[j]) {
arr[j+1] = arr[j];
--j;
}
arr[j+1] = key;
}
}
}
分組插入排序,縮小增量序列,平均複雜度可達 O(n^1.3) ~ O(n^1.5),是插入排序的實用化擴展。
std::sort 實作常在小區間切換至插入排序 (如 GCC libstdc++ 閾值 16)| 演算法 | 穩定 | 最佳情況 | 適應性 | 實務表現 |
|---|---|---|---|---|
| 插入排序 | ✓ | O(n) | 高 | 小/近乎有序最佳 |
| 氣泡排序 | ✓ | O(n) | 中 | 教學用,實務較差 |
| 選擇排序 | ✗ | O(n²) | 無 | 交換次數最少 (O(n)) |
直接插入排序雖非大規模排序首選,但其簡單、穩定、適應性強、零額外空間的特性,使其在特定場景 (小陣列、近乎有序、線上排序、混合排序基礎組件) 具有不可替代的價值。掌握其變體 (二分插入、希爾排序) 並理解標準庫如何利用它,是演算法工程師的基本功。