算法中的离散化
离散化
在算法竞赛中,我们经常遇到这样的问题:数据范围巨大(例如 10^9),但实际出现的不同数值却很少(例如 10^5)。如果直接按照值域开数组,内存会爆炸,时间也会超限。这时就需要一种技巧 —— 离散化。本文将深入讲解离散化的概念、实现方法,并提供完整的 C++ 代码示例。
什么是离散化?
离散化,就是把无限空间(或极大范围)中的有限个体,映射到紧凑的有限区间中,同时不改变它们的相对大小顺序。
通俗的比喻:你有一张全国所有城市的气温表,温度值精确到小数点后 8 位,但一共只记录了 10 万个城市。你想统计“不同温度”的出现次数。显然不能用 [-100, 100] 的实数轴开数组。离散化会先将这些温度值排序、去重,然后给每个温度分配一个 1, 2, 3, ... 的整数编号。之后用这个编号作为数组下标,一切变得简单高效。
为什么算法竞赛离不开离散化?
- 内存限制:直接以原值为下标往往需要巨大的数组(例如
int a[1e9]需要 4GB)。 - 时间优化:很多算法的复杂度与值域大小成正比(如计数排序、树状数组、线段树)。离散化后将值域压缩为
O(N),使这些算法可行。 - 忽略无关细节:当问题只关心元素之间的大小关系,而不关心具体数值的差值时,离散化是完美的简化。
离散化的标准三步骤(C++ 实现)
- 收集所有待离散化的值(例如原始数组
a[])。 - 排序 + 去重。
- 对每个原值,通过二分查找得到它的排名(索引)。
C++ 标准库提供了 std::sort、std::unique 和 std::lower_bound,让实现变得极其简洁。
基础代码模板
#include <bits/stdc++.h>
using namespace std;
// 离散化函数:将原始数组 a 映射为从 1 开始的连续整数,结果存放在 b 中
vector<int> discretize(const vector<int>& a) {
vector<int> tmp = a; // 复制一份
sort(tmp.begin(), tmp.end()); // 排序
tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); // 去重
vector<int> b(a.size());
for (size_t i = 0; i < a.size(); ++i) {
// lower_bound 返回第一个 >= a[i] 的迭代器,减去 tmp.begin() 得到下标(从0开始)
// 如果想要从 1 开始,就 +1
b[i] = lower_bound(tmp.begin(), tmp.end(), a[i]) - tmp.begin() + 1;
}
return b;
}
int main() {
vector<int> original = {1000000000, 2, 5, 2, 1, 1000000000, 3};
vector<int> compressed = discretize(original);
cout << "原始数据:";
for (int v : original) cout << v << " ";
cout << endl;
cout << "离散化后:";
for (int v : compressed) cout << v << " ";
cout << endl;
return 0;
}

浙公网安备 33010602011771号