算法中的离散化

离散化

在算法竞赛中,我们经常遇到这样的问题:数据范围巨大(例如 10^9),但实际出现的不同数值却很少(例如 10^5)。如果直接按照值域开数组,内存会爆炸,时间也会超限。这时就需要一种技巧 —— 离散化。本文将深入讲解离散化的概念、实现方法,并提供完整的 C++ 代码示例。

什么是离散化?

离散化,就是把无限空间(或极大范围)中的有限个体,映射到紧凑的有限区间中,同时不改变它们的相对大小顺序

通俗的比喻:你有一张全国所有城市的气温表,温度值精确到小数点后 8 位,但一共只记录了 10 万个城市。你想统计“不同温度”的出现次数。显然不能用 [-100, 100] 的实数轴开数组。离散化会先将这些温度值排序、去重,然后给每个温度分配一个 1, 2, 3, ... 的整数编号。之后用这个编号作为数组下标,一切变得简单高效。

为什么算法竞赛离不开离散化?

  • 内存限制:直接以原值为下标往往需要巨大的数组(例如 int a[1e9] 需要 4GB)。
  • 时间优化:很多算法的复杂度与值域大小成正比(如计数排序、树状数组、线段树)。离散化后将值域压缩为 O(N),使这些算法可行。
  • 忽略无关细节:当问题只关心元素之间的大小关系,而不关心具体数值的差值时,离散化是完美的简化。

离散化的标准三步骤(C++ 实现)

  1. 收集所有待离散化的值(例如原始数组 a[])。
  2. 排序 + 去重
  3. 对每个原值,通过二分查找得到它的排名(索引)

C++ 标准库提供了 std::sortstd::uniquestd::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;
}
posted @ 2026-06-08 17:25  十七code  阅读(14)  评论(0)    收藏  举报