数据离散化
843 字
4 分钟
数据离散化

数据离散化
离散化用来处理“值域很大,但实际出现的数很少”的问题。
比如坐标范围是 0 到 10^9,但题目只给了 10^5 个点。如果直接开数组,肯定开不下。这时就可以把这些大坐标映射到连续的小下标。
1、离散化是什么
假设原始坐标是:
1 234 542 8245 1000000000离散化后可以变成:
1 2 3 4 5它不关心原值之间到底差多少,只关心它们的相对顺序。
2、离散化的三个步骤
1. 收集所有会用到的坐标2. 排序并去重3. 用二分找到每个坐标对应的新下标这里“所有会用到的坐标”很重要。查询边界也要收集进去。
3、排序去重模板
vector<int> alls;
sort(alls.begin(), alls.end());alls.erase(unique(alls.begin(), alls.end()), alls.end());unique 只会把重复元素挪到后面,并返回去重后末尾位置,所以还要配合 erase 真正删除。
4、二分映射模板
int find(int x) { int l = 0; int r = alls.size() - 1;
while (l < r) { int mid = (l + r) >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; }
return r + 1;}这里返回 r + 1,是因为后面要配合前缀和使用,数组从 1 开始更方便。
5、典型问题
题目给出:
n次加法操作:在位置x上加c。m次查询:查询区间[l, r]的和。
坐标可能很大,但操作和查询次数有限,所以可以离散化。
6、完整 C++ 模板
#include <bits/stdc++.h>using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int n, m;int a[N], s[N];
vector<int> alls;vector<PII> add_ops;vector<PII> query_ops;
int find_index(int x) { int l = 0; int r = alls.size() - 1;
while (l < r) { int mid = (l + r) >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; }
return r + 1;}
int main() { cin >> n >> m;
for (int i = 0; i < n; i++) { int x, c; cin >> x >> c; add_ops.push_back({x, c}); alls.push_back(x); }
for (int i = 0; i < m; i++) { int l, r; cin >> l >> r; query_ops.push_back({l, r}); alls.push_back(l); alls.push_back(r); }
sort(alls.begin(), alls.end()); alls.erase(unique(alls.begin(), alls.end()), alls.end());
for (auto item : add_ops) { int x = find_index(item.first); a[x] += item.second; }
for (int i = 1; i <= (int)alls.size(); i++) { s[i] = s[i - 1] + a[i]; }
for (auto item : query_ops) { int l = find_index(item.first); int r = find_index(item.second); cout << s[r] - s[l - 1] << endl; }
return 0;}7、为什么查询边界也要加入 alls
如果只收集加法操作的位置,查询 [l, r] 的 l 和 r 可能根本不在离散数组中。
比如只有位置 100 有值,但查询是 [1, 1000000000]。如果不收集 1 和 1000000000,就无法把查询边界映射到正确下标。
8、容易写错的地方
(1)忘记去重
重复坐标如果不去掉,会影响二分映射和前缀和下标。
(2)返回下标是否加 1 要统一
如果前缀和从 1 开始,就返回 r + 1。
如果数组从 0 开始,就返回 r。
两种写法都可以,但整篇代码必须统一。
(3)alls.size() 是无符号类型
写循环时可以转成 int:
for (int i = 1; i <= (int)alls.size(); i++)避免类型比较警告。
总结
离散化的目的不是改变大小关系,而是把稀疏的大坐标压缩成连续小下标。
收集坐标 -> 排序去重 -> 二分映射 -> 在小下标数组上处理文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














