graph LR
A["O(1)\n常数"]
B["O(log n)\n对数"]
C["O(n)\n线性"]
D["O(n log n)\n线性对数"]
E["O(n²)\n平方"]
F["O(2ⁿ)\n指数"]
A --> B --> C --> D --> E --> F
style A fill:#B5EAD7,stroke:#80CBC4,color:#333
style B fill:#C7CEEA,stroke:#9FA8DA,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style F fill:#E8D5F5,stroke:#CE93D8,color:#333
// 基础版:双循环 + 交换 voidbubble_sort_v1(std::vector<int>& a){ int n = a.size(); for (int i = 0; i < n - 1; ++i) { for (int j = 0; j < n - 1 - i; ++j) { if (a[j] > a[j + 1]) { std::swap(a[j], a[j + 1]); // 相邻交换 } } } }
版本二:优化版(提前退出)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 优化版:如果一轮没有交换,说明已有序 voidbubble_sort_v2(std::vector<int>& a){ int n = a.size(); bool swapped; for (int i = 0; i < n - 1; ++i) { swapped = false; for (int j = 0; j < n - 1 - i; ++j) { if (a[j] > a[j + 1]) { std::swap(a[j], a[j + 1]); swapped = true; } } if (!swapped) break; // 本轮无交换,已有序 } }
版本三:双向冒泡(鸡尾酒排序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 鸡尾酒排序:双向冒泡,减少排序回合数 voidcocktail_sort(std::vector<int>& a){ int left = 0, right = a.size() - 1; while (left < right) { // 从左到右:大元素冒泡到 right for (int i = left; i < right; ++i) { if (a[i] > a[i + 1]) std::swap(a[i], a[i + 1]); } --right; // 从右到左:小元素冒泡到 left for (int i = right; i > left; --i) { if (a[i] < a[i - 1]) std::swap(a[i], a[i - 1]); } ++left; } }
冒泡排序特性表
维度
值
最好
O(n)(已有序,v2 加 early exit)
平均
O(n²)
最坏
O(n²)
空间
O(1) 原地
稳定性
✅ 稳定
适用
教学、数据基本有序的小数据集
3.2 选择排序(Selection Sort)
核心思想:每轮从未排序区间选最小元素,放到已排序区间末尾。
版本一:基础版
1 2 3 4 5 6 7 8 9 10 11
// 基础版:每轮选最小值交换到已排序末尾 voidselection_sort_v1(std::vector<int>& a){ int n = a.size(); for (int i = 0; i < n - 1; ++i) { int min_idx = i; for (int j = i + 1; j < n; ++j) { if (a[j] < a[min_idx]) min_idx = j; } std::swap(a[i], a[min_idx]); // 只交换一次 } }
版本二:双向选择(同时选最小和最大)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 双向选择:每轮同时选最小和最大 voidselection_sort_v2(std::vector<int>& a){ int left = 0, right = a.size() - 1; while (left < right) { int min_idx = left, max_idx = right; for (int i = left; i <= right; ++i) { if (a[i] < a[min_idx]) min_idx = i; if (a[i] > a[max_idx]) max_idx = i; } std::swap(a[left], a[min_idx]); // 如果 max_idx == left,swap 后 max 已到 min_idx if (max_idx == left) max_idx = min_idx; std::swap(a[right], a[max_idx]); ++left; --right; } }
选择排序特性表
维度
值
最好
O(n²)
平均
O(n²)
最坏
O(n²)
空间
O(1) 原地
稳定性
❌ 不稳定
交换次数
最多 n-1 次
适用
交换成本远高于比较成本的场景(如写 Flash)
3.3 插入排序(Insertion Sort)
核心思想:把数组分为「已排序区间」和「未排序区间」,从未排序区间取元素,在已排序区间找到插入位置。
版本一:基础版
1 2 3 4 5 6 7 8 9 10 11 12 13
// 基础版:从后往前找插入位置 voidinsertion_sort_v1(std::vector<int>& a){ int n = a.size(); for (int i = 1; i < n; ++i) { int key = a[i]; int j = i - 1; while (j >= 0 && a[j] > key) { a[j + 1] = a[j]; // 后移 --j; } a[j + 1] = key; } }
版本二:二分插入(用二分找位置)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 二分插入:用二分找位置,但插入仍需 O(n) 后移 voidbinary_insertion_sort(std::vector<int>& a){ int n = a.size(); for (int i = 1; i < n; ++i) { int key = a[i]; int lo = 0, hi = i - 1; while (lo <= hi) { int mid = (lo + hi) / 2; if (a[mid] <= key) lo = mid + 1; else hi = mid - 1; } // lo 是插入位置 for (int j = i - 1; j >= lo; --j) a[j + 1] = a[j]; a[lo] = key; } }
版本三:STL 思路(无哨兵)
1 2 3 4 5 6 7 8 9 10
// 与 std::sort 内层逻辑类似的版本 voidinsertion_sort_stl(std::vector<int>& a){ for (int i = 1; i < (int)a.size(); ++i) { int j = i; while (j > 0 && a[j - 1] > a[j]) { std::swap(a[j - 1], a[j]); // 用 swap 而不是后移 --j; } } }
核心思想:插入排序的优化。把数组按**间隔(gap)**分组,对每组做插入排序,逐步缩小 gap 到 1。
版本一:基础版(gap = n/2, n/4, …, 1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 基础希尔:gap 折半 voidshell_sort_v1(std::vector<int>& a){ int n = a.size(); for (int gap = n / 2; gap > 0; gap /= 2) { // 对每个子序列做插入排序 for (int i = gap; i < n; ++i) { int key = a[i]; int j = i - gap; while (j >= 0 && a[j] > key) { a[j + gap] = a[j]; j -= gap; } a[j + gap] = key; } } }
版本二:Knuth 序列(gap = 1, 4, 13, 40, …)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Knuth 序列:h = 3*h + 1,性能更好 voidshell_sort_knuth(std::vector<int>& a){ int n = a.size(); int gap = 1; while (gap < n / 3) gap = 3 * gap + 1; // 1, 4, 13, 40, 121, ...
while (gap >= 1) { for (int i = gap; i < n; ++i) { int key = a[i]; int j = i - gap; while (j >= 0 && a[j] > key) { a[j + gap] = a[j]; j -= gap; } a[j + gap] = key; } gap /= 3; } }
// 自底向上:用 gap = 1, 2, 4, 8... 逐步合并 voidmerge_sort_bottom_up(std::vector<int>& a){ int n = a.size(); std::vector<int> tmp(n); for (int gap = 1; gap < n; gap *= 2) { for (int lo = 0; lo < n - gap; lo += 2 * gap) { int mid = lo + gap - 1; int hi = std::min(lo + 2 * gap - 1, n - 1); // 合并 a[lo..mid] 和 a[mid+1..hi] int i = lo, j = mid + 1, k = lo; while (i <= mid && j <= hi) { if (a[i] <= a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } while (i <= mid) tmp[k++] = a[i++]; while (j <= hi) tmp[k++] = a[j++]; // 拷回 for (int x = lo; x <= hi; ++x) a[x] = tmp[x]; } } }
版本三:原地归并(高级优化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 原地归并:O(1) 空间但常数极大 // 一般不推荐,仅作了解 voidmerge_inplace(std::vector<int>& a, int lo, int mid, int hi){ int i = lo, j = mid + 1; while (i < j && j <= hi) { // 找到 a[i] > a[j] 的位置 while (i < j && a[i] <= a[j]) ++i; // 把 a[j..] 旋转到 a[i..] 之前 int k = j; while (k <= hi && a[k] < a[i]) ++k; std::reverse(a.begin() + i, a.begin() + k); i += (k - j); j = k; } }
// Lomuto 分区:选最右元素为 pivot intpartition_lomuto(std::vector<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) { ++i; std::swap(a[i], a[j]); } } std::swap(a[i + 1], a[hi]); return i + 1; }
voidquick_sort_v1(std::vector<int>& a, int lo, int hi){ if (lo >= hi) return; int p = partition_lomuto(a, lo, hi); quick_sort_v1(a, lo, p - 1); quick_sort_v1(a, p + 1, hi); }
// Hoare 分区 + 三数取中 intmedian_of_three(std::vector<int>& a, int lo, int hi){ int mid = lo + (hi - lo) / 2; if (a[lo] > a[mid]) std::swap(a[lo], a[mid]); if (a[lo] > a[hi]) std::swap(a[lo], a[hi]); if (a[mid] > a[hi]) std::swap(a[mid], a[hi]); // a[mid] 是中位数,把它放到 lo+1 位置 std::swap(a[mid], a[lo + 1]); return a[lo + 1]; }
intpartition_hoare(std::vector<int>& a, int lo, int hi){ int pivot = median_of_three(a, lo, hi); int i = lo, j = hi; while (true) { while (a[i] < pivot) ++i; while (a[j] > pivot) --j; if (i >= j) return j; std::swap(a[i], a[j]); ++i; --j; } }
voidquick_sort_v2(std::vector<int>& a, int lo, int hi){ if (lo + 16 <= hi) { // 区间大于 16 用快排 int p = partition_hoare(a, lo, hi); quick_sort_v2(a, lo, p); quick_sort_v2(a, p + 1, hi); } elseif (lo < hi) { // 小区间用插入排序(STL 的策略) insertion_sort_v1(std::vector<int>(a.begin() + lo, a.begin() + hi + 1)); // 实际应该是原地插入排序,这里简化展示 } }
版本三:随机化快排(防最坏)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 随机选 pivot:防止有序数组导致 O(n²) intpartition_random(std::vector<int>& a, int lo, int hi){ // 随机选一个元素换到末尾 int rnd = lo + rand() % (hi - lo + 1); std::swap(a[rnd], a[hi]); returnpartition_lomuto(a, lo, hi); }
voidquick_sort_random(std::vector<int>& a, int lo, int hi){ if (lo >= hi) return; int p = partition_random(a, lo, hi); quick_sort_random(a, lo, p - 1); quick_sort_random(a, p + 1, hi); }
// 调整:以 i 为根的子树保持大顶堆性质 voidheapify(std::vector<int>& a, int n, int i){ int largest = i; int left = 2 * i + 1; int right = 2 * i + 2;
if (left < n && a[left] > a[largest]) largest = left; if (right < n && a[right] > a[largest]) largest = right;
if (largest != i) { std::swap(a[i], a[largest]); heapify(a, n, largest); // 递归调整被影响的子树 } }
voidheap_sort_v1(std::vector<int>& a){ int n = a.size(); // 1. 建堆:从最后一个非叶子节点开始 for (int i = n / 2 - 1; i >= 0; --i) { heapify(a, n, i); } // 2. 排序:反复取堆顶 for (int i = n - 1; i > 0; --i) { std::swap(a[0], a[i]); // 堆顶(最大)放到末尾 heapify(a, i, 0); // 调整剩余堆 } }
// Floyd 优化:sift down 改成迭代,避免递归 voidheapify_iterative(std::vector<int>& a, int n, int i){ while (true) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2;
if (left < n && a[left] > a[largest]) largest = left; if (right < n && a[right] > a[largest]) largest = right;
if (largest == i) break; std::swap(a[i], a[largest]); i = largest; } }
voidheap_sort_v2(std::vector<int>& a){ int n = a.size(); for (int i = n / 2 - 1; i >= 0; --i) { heapify_iterative(a, n, i); } for (int i = n - 1; i > 0; --i) { std::swap(a[0], a[i]); heapify_iterative(a, i, 0); } }
版本三:STL priority_queue 思路
1 2 3 4 5 6 7 8 9 10 11 12 13
// STL priority_queue 内部用堆实现 // 完整堆排序 = 建堆 + 反复 pop std::vector<int> heap_sort_stl(std::vector<int> a){ std::priority_queue<int> pq; for (int x : a) pq.push(x); std::vector<int> result; result.reserve(a.size()); while (!pq.empty()) { result.push_back(pq.top()); pq.pop(); } return result; // 默认大顶堆,结果是降序 }
// 需求:先按金额排序,再按下单时间排序 // 如果两次排序都希望稳定,要用 stable_sort std::vector<Order> orders = {...}; // 错误做法:sort 后 sort,第一次排序的相对顺序丢了 std::sort(orders.begin(), orders.end(), [](const Order& a, const Order& b) { return a.amount > b.amount; }); std::sort(orders.begin(), orders.end(), [](const Order& a, const Order& b) { return a.time < b.time; }); // 正确做法:用 stable_sort std::stable_sort(orders.begin(), orders.end(), [](const Order& a, const Order& b) { return a.time < b.time; }); std::stable_sort(orders.begin(), orders.end(), [](const Order& a, const Order& b) { return a.amount > b.amount; });
场景 2:基数排序的前置条件
基数排序(Radix Sort)的子过程必须用稳定排序,否则个位排完十位排,百位结果全乱。
场景 3:数据库多列排序
ORDER BY score DESC, name ASC 的底层实现依赖稳定性。
6.4 把不稳定的排序改成稳定
技巧:加一个额外的序号字段,作为最终 tie-breaker。
1 2 3 4 5 6 7 8 9 10
// 把不稳定的堆排序改成稳定 structStableItem { int key; int index; // 原顺序 booloperator<(const StableItem& o) const { if (key != o.key) return key < o.key; return index > o.index; // index 小者排前面 } }; // 用堆排序 StableItem,因为加了 index tie-breaker,整体稳定
七、查找算法:顺序 / 二分 / 插值
7.1 顺序查找(Linear Search)
核心思想:从头到尾逐个比较。适用任何数据结构,无需排序。
基础版
1 2 3 4 5 6 7
// 最朴素的顺序查找 intlinear_search(const std::vector<int>& a, int target){ for (int i = 0; i < (int)a.size(); ++i) { if (a[i] == target) return i; } return-1; }
优化版:哨兵模式
1 2 3 4 5 6 7 8 9 10 11 12
// 哨兵:把判断 i < n 和 a[i] == target 合并 intlinear_search_sentinel(std::vector<int>& a, int target){ int n = a.size(); if (n == 0) return-1; int last = a[n - 1]; // 保存末尾元素 a[n - 1] = target; // 设哨兵 int i = 0; while (a[i] != target) ++i; // 无需判断越界 a[n - 1] = last; // 恢复末尾 if (i < n - 1 || a[n - 1] == target) return i; return-1; }
顺序查找特性表
维度
值
最好
O(1)(第一个就是)
平均
O(n)
最坏
O(n)
空间
O(1)
数据要求
无
适用
无序数据、链表、流式数据
7.2 二分查找(Binary Search)
核心思想:在有序数组中,每次比较中间元素,把范围减半。
版本一:基础版
1 2 3 4 5 6 7 8 9 10 11
// 基础二分(闭区间 [lo, hi]) intbinary_search_v1(const std::vector<int>& a, int target){ int lo = 0, hi = (int)a.size() - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; // 防 (lo+hi) 溢出 if (a[mid] == target) return mid; elseif (a[mid] < target) lo = mid + 1; else hi = mid - 1; } return-1; }
版本二:递归版
1 2 3 4 5 6 7 8 9 10 11 12
// 递归版(更易理解,但有栈开销) intbinary_search_rec(const std::vector<int>& a, int target, int lo, int hi){ if (lo > hi) return-1; int mid = lo + (hi - lo) / 2; if (a[mid] == target) return mid; if (a[mid] < target) returnbinary_search_rec(a, target, mid + 1, hi); returnbinary_search_rec(a, target, lo, mid - 1); }
intbinary_search_v2(const std::vector<int>& a, int target){ returnbinary_search_rec(a, target, 0, a.size() - 1); }
// 找 target 的最左位置(用于统计出现次数) intlower_bound(const std::vector<int>& a, int target){ int lo = 0, hi = (int)a.size(); // [lo, hi) 半开区间 while (lo < hi) { int mid = lo + (hi - lo) / 2; if (a[mid] < target) lo = mid + 1; else hi = mid; // 保留 mid } return lo; // lo == hi,即插入点 }
// 找 target 的最右位置 intupper_bound(const std::vector<int>& a, int target){ int lo = 0, hi = (int)a.size(); while (lo < hi) { int mid = lo + (hi - lo) / 2; if (a[mid] <= target) lo = mid + 1; else hi = mid; } return lo; }
// 统计 target 出现次数 intcount_occurrences(const std::vector<int>& a, int target){ returnupper_bound(a, target) - lower_bound(a, target); }
二分查找的 10 种写法对照表
写法
区间
mid 计算
终止条件
基础版
[lo, hi]
(lo+hi)/2
lo > hi
改进版(防溢出)
[lo, hi]
lo + (hi-lo)/2
lo > hi
半开区间
[lo, hi)
lo + (hi-lo)/2
lo >= hi
STL lower_bound
[lo, hi)
lo + (hi-lo)/2
lo >= hi
二分查找特性表
维度
值
最好
O(1)(第一个 mid 就是)
平均
O(log n)
最坏
O(log n)
空间
O(1) 迭代 / O(log n) 递归
数据要求
必须有序
适用
大数据 + 有序 + 静态
7.3 插值查找(Interpolation Search)
核心思想:二分查找的优化。根据数据分布估算目标位置,而不是机械地取中间。对均匀分布数据极快。
版本一:基础版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 插值查找:mid = lo + (target - a[lo]) * (hi - lo) / (a[hi] - a[lo]) intinterpolation_search(const std::vector<int>& a, int target){ int lo = 0, hi = (int)a.size() - 1; while (lo <= hi && target >= a[lo] && target <= a[hi]) { if (lo == hi) { if (a[lo] == target) return lo; return-1; } // 插值公式 int pos = lo + (int)(((longlong)(target - a[lo]) * (hi - lo)) / (a[hi] - a[lo])); if (a[pos] == target) return pos; if (a[pos] < target) lo = pos + 1; else hi = pos - 1; } return-1; }
版本二:字符串字典插值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 字符串在字典中的插值查找(电话簿) intstring_interp_search(const std::vector<std::string>& dict, const std::string& target){ int lo = 0, hi = (int)dict.size() - 1; while (lo <= hi && target >= dict[lo] && target <= dict[hi]) { if (lo == hi) { return dict[lo] == target ? lo : -1; } // 估算位置(字典序) int pos = lo + (int)(((longlong)(target[0] - dict[lo][0]) * (hi - lo)) / (dict[hi][0] - dict[lo][0])); if (pos < lo || pos > hi) break; if (dict[pos] == target) return pos; if (dict[pos] < target) lo = pos + 1; else hi = pos - 1; } return-1; }
三种查找算法对比表
维度
顺序查找
二分查找
插值查找
最好
O(1)
O(1)
O(1)
平均
O(n)
O(log n)
O(log log n)(均匀分布)
最坏
O(n)
O(log n)
O(n)(分布极端不均)
数据要求
无
有序
有序 + 均匀分布
适用
小数据 / 无序
通用有序查找
大数据 + 均匀分布
八、TopK 问题:4 种解法 + 完整对比
TopK 问题:从 n 个数中找出最大(或最小)的 k 个。
8.1 解法一:全排序
思路:直接排序,取前 k 个。
1 2 3 4 5
// 解法一:全排序,时间 O(n log n) std::vector<int> topk_by_sort(std::vector<int> a, int k){ std::sort(a.begin(), a.end(), std::greater<int>()); return std::vector<int>(a.begin(), a.begin() + k); }
// 解法二:小顶堆,时间 O(n log k),推荐! std::vector<int> topk_by_heap(const std::vector<int>& a, int k){ // 小顶堆:堆顶是堆中最小的 std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap; for (int x : a) { if ((int)min_heap.size() < k) { min_heap.push(x); } elseif (x > min_heap.top()) { // 当前元素比堆顶(最小门槛)大,替换 min_heap.pop(); min_heap.push(x); } } // 堆里就是 TopK std::vector<int> result; result.reserve(k); while (!min_heap.empty()) { result.push_back(min_heap.top()); min_heap.pop(); } std::reverse(result.begin(), result.end()); // 大到小 return result; }
TopK 堆维护流程图
graph TB
A["遍历数据流\n3, 1, 5, 8, 2, 9, 7"]
B{"堆大小 < k?"}
C["push x\n建堆"]
D{"x > 堆顶?"}
E["pop 堆顶\npush x"]
F["继续遍历"]
G["输出堆"]
A --> B
B -->|"是"| C --> F
B -->|"否"| D
D -->|"是"| E --> F
D -->|"否"| F
F -->|"还有数据"| B
F -->|"遍历完"| G
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style F fill:#E8D5F5,stroke:#CE93D8,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,stroke-width:3px,color:#333
// 解法三:QuickSelect,平均 O(n),最坏 O(n²) intpartition_qs(std::vector<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) ++i, std::swap(a[i], a[j]); // 降序 } std::swap(a[i + 1], a[hi]); return i + 1; }
voidquick_select(std::vector<int>& a, int lo, int hi, int k){ // 找到第 k 大的元素(0-indexed) if (lo >= hi) return; int p = partition_qs(a, lo, hi); if (p == k) return; // 找到了 if (p < k) quick_select(a, p + 1, hi, k); elsequick_select(a, lo, p - 1, k); }
std::vector<int> topk_by_quickselect(std::vector<int> a, int k){ quick_select(a, 0, a.size() - 1, k - 1); // 第 k 大是 index k-1 return std::vector<int>(a.begin(), a.begin() + k); }
8.4 解法四:计数排序 / 桶排序
思路:当数据范围有限时,统计每个值的出现次数,逆序累加找到 TopK。
1 2 3 4 5 6 7 8 9 10 11 12 13
// 解法四:计数排序(数据范围小时最优) std::vector<int> topk_by_counting(const std::vector<int>& a, int k, int max_val){ std::vector<int> cnt(max_val + 1, 0); for (int x : a) cnt[x]++;
std::vector<int> result; for (int v = max_val; v >= 0 && (int)result.size() < k; --v) { for (int c = 0; c < cnt[v] && (int)result.size() < k; ++c) { result.push_back(v); } } return result; }
// 1. 冒泡 voidbubble_sort(std::vector<int>& a){ int n = a.size(); bool swapped; for (int i = 0; i < n - 1; ++i) { swapped = false; for (int j = 0; j < n - 1 - i; ++j) { if (a[j] > a[j + 1]) { std::swap(a[j], a[j + 1]); swapped = true; } } if (!swapped) break; } }
// 2. 选择 voidselection_sort(std::vector<int>& a){ int n = a.size(); for (int i = 0; i < n - 1; ++i) { int min_idx = i; for (int j = i + 1; j < n; ++j) { if (a[j] < a[min_idx]) min_idx = j; } std::swap(a[i], a[min_idx]); } }
// 3. 插入 voidinsertion_sort(std::vector<int>& a){ for (int i = 1; i < (int)a.size(); ++i) { int key = a[i]; int j = i - 1; while (j >= 0 && a[j] > key) { a[j + 1] = a[j]; --j; } a[j + 1] = key; } }
// 4. 希尔 voidshell_sort(std::vector<int>& a){ int n = a.size(); for (int gap = n / 2; gap > 0; gap /= 2) { for (int i = gap; i < n; ++i) { int key = a[i]; int j = i - gap; while (j >= 0 && a[j] > key) { a[j + gap] = a[j]; j -= gap; } a[j + gap] = key; } } }
// 5. 归并 voidmerge(std::vector<int>& a, int lo, int mid, int hi, std::vector<int>& tmp){ int i = lo, j = mid + 1, k = lo; while (i <= mid && j <= hi) { if (a[i] <= a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } while (i <= mid) tmp[k++] = a[i++]; while (j <= hi) tmp[k++] = a[j++]; for (int x = lo; x <= hi; ++x) a[x] = tmp[x]; } voidmerge_sort_impl(std::vector<int>& a, int lo, int hi, std::vector<int>& tmp){ if (lo >= hi) return; int mid = lo + (hi - lo) / 2; merge_sort_impl(a, lo, mid, tmp); merge_sort_impl(a, mid + 1, hi, tmp); merge(a, lo, mid, hi, tmp); } voidmerge_sort(std::vector<int>& a){ if (a.empty()) return; std::vector<int> tmp(a.size()); merge_sort_impl(a, 0, a.size() - 1, tmp); }
// 6. 快排 intpartition(std::vector<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) { ++i; std::swap(a[i], a[j]); } } std::swap(a[i + 1], a[hi]); return i + 1; } voidquick_sort_impl(std::vector<int>& a, int lo, int hi){ if (lo >= hi) return; int p = partition(a, lo, hi); quick_sort_impl(a, lo, p - 1); quick_sort_impl(a, p + 1, hi); } voidquick_sort(std::vector<int>& a){ if (!a.empty()) quick_sort_impl(a, 0, a.size() - 1); }
// 7. 堆排 voidheapify(std::vector<int>& a, int n, int i){ while (true) { int largest = i; int l = 2 * i + 1, r = 2 * i + 2; if (l < n && a[l] > a[largest]) largest = l; if (r < n && a[r] > a[largest]) largest = r; if (largest == i) break; std::swap(a[i], a[largest]); i = largest; } } voidheap_sort(std::vector<int>& a){ int n = a.size(); for (int i = n / 2 - 1; i >= 0; --i) heapify(a, n, i); for (int i = n - 1; i > 0; --i) { std::swap(a[0], a[i]); heapify(a, i, 0); } }
// ==================== 查找 ====================
intlinear_search(const std::vector<int>& a, int target){ for (int i = 0; i < (int)a.size(); ++i) { if (a[i] == target) return i; } return-1; }
intbinary_search(const std::vector<int>& a, int target){ int lo = 0, hi = (int)a.size() - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (a[mid] == target) return mid; if (a[mid] < target) lo = mid + 1; else hi = mid - 1; } return-1; }
intinterpolation_search(const std::vector<int>& a, int target){ int lo = 0, hi = (int)a.size() - 1; while (lo <= hi && target >= a[lo] && target <= a[hi]) { if (a[lo] == a[hi]) { if (a[lo] == target) return lo; return-1; } int pos = lo + (int)(((longlong)(target - a[lo]) * (hi - lo)) / (a[hi] - a[lo])); if (a[pos] == target) return pos; if (a[pos] < target) lo = pos + 1; else hi = pos - 1; } return-1; }
// ==================== TopK ====================
std::vector<int> topk_by_heap(const std::vector<int>& a, int k){ std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap; for (int x : a) { if ((int)min_heap.size() < k) min_heap.push(x); elseif (x > min_heap.top()) { min_heap.pop(); min_heap.push(x); } } std::vector<int> result; while (!min_heap.empty()) { result.push_back(min_heap.top()); min_heap.pop(); } std::reverse(result.begin(), result.end()); return result; }
intpartition_qs(std::vector<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) ++i, std::swap(a[i], a[j]); } std::swap(a[i + 1], a[hi]); return i + 1; }
voidquick_select_impl(std::vector<int>& a, int lo, int hi, int k){ if (lo >= hi) return; int p = partition_qs(a, lo, hi); if (p == k) return; if (p < k) quick_select_impl(a, p + 1, hi, k); elsequick_select_impl(a, lo, p - 1, k); }
std::vector<int> topk_by_quickselect(std::vector<int> a, int k){ quick_select_impl(a, 0, a.size() - 1, k - 1); return std::vector<int>(a.begin(), a.begin() + k); }
// ==================== 测试工具 ====================
std::vector<int> random_vec(int n, int seed = 42){ std::mt19937 rng(seed); std::uniform_int_distribution<int> dist(0, 99999); std::vector<int> v(n); for (int& x : v) x = dist(rng); return v; }
voidtest_sort(const std::string& name, void (*sort_fn)(std::vector<int>&), const std::vector<int>& data){ auto a = data; auto start = std::chrono::high_resolution_clock::now(); sort_fn(a); auto end = std::chrono::high_resolution_clock::now(); double ms = std::chrono::duration<double, std::milli>(end - start).count(); assert(std::is_sorted(a.begin(), a.end())); std::cout << std::left << std::setw(20) << name << std::right << std::setw(8) << std::fixed << std::setprecision(2) << ms << " ms\n"; }
voidtest_search(const std::string& name, int (*search_fn)(const std::vector<int>&, int), const std::vector<int>& data, int target){ int idx = search_fn(data, target); std::cout << std::left << std::setw(25) << name << "target=" << target << " -> index=" << idx << "\n"; }