数据离散化

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++)

避免类型比较警告。

总结#

离散化的目的不是改变大小关系,而是把稀疏的大坐标压缩成连续小下标。

收集坐标 -> 排序去重 -> 二分映射 -> 在小下标数组上处理

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

数据离散化
https://blog.tjcheng.cn/posts/migrated/old-82/
作者
TengJinCheng
发布于
2025-10-04
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录