排序与离散化
💡 核心思想
排序是将数据按特定顺序排列的操作,是算法竞赛的基础工具。离散化是将大范围的数值映射到紧凑的小范围,常用于坐标压缩,是处理大数据范围的重要技巧。
竞赛中几乎不会让你手写排序算法——直接用
sort()。但你必须理解各种排序的特点:稳定排序 vs 不稳定排序、$O(n \log n)$ 的下界。离散化是个非常实用的技巧:当值域很大但实际值很少时(比如坐标是 $10^9$ 级别但只有 $10^5$ 个),离散化后用下标操作就方便多了。🎯 直觉理解
排序就像整理书架上的书——按作者名排序。离散化就像把"第1排第1000000000列"简化为"第3个位置"——重要的不是绝对位置,而是相对顺序。
📝 算法流程
- 排序:使用 STL sort(),自定义比较函数
- 离散化:排序 → 去重 → 二分查找映射
- 注意 stable_sort() 保证相等元素原有顺序
$$\text{离散化:} a_i \mapsto \text{rank}(a_i) = \text{lower_bound(sorted, } a_i) - sorted.begin() + 1$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | 排序 $O(n \log n)$,离散化 $O(n \log n)$ |
| 空间 | $O(n)$ |
💻 参考实现(C++)
C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
// 排序 + 离散化模板
int main() {
int n; cin >> n;
vector<int> a(n), b;
for (auto& x : a) cin >> x;
b = a;
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end()); // 去重
// 离散化:a[i] 映射为 1-based rank
for (auto& x : a) {
x = lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
}
for (auto x : a) cout << x << ' ';
return 0;
}⚠️ 常见坑点
离散化忘记去重导致映射错误
自定义比较函数不满足严格弱序(< 中的等号问题)
sort() 的比较函数里用 <= 导致运行时错误
结构体排序忘记重载 < 或传比较函数
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P1059 明明的随机数 | 洛谷 | CSP-J | 排序去重 |
| P1966 火柴排队 | 洛谷 | CSP-S | 离散化+逆序对 |