离散化 算法 思路+模板代码

问题背景

序列中的元素值域非常大,但是序列中元素的个数非常小
满足值域非常大,但个数非常小
非常的松散
序列元素的相对大小位置)的作用>绝对大小值是多少)的作用
因为非常的松散,开辟值域范围大小的数组来存储它们非常的不划算且不现实
所以应该想出一个方法,把松散的序列映射成等价紧凑的序列
这种方法,叫做离散化


思路(离散化的步骤)

首先,我们定义一个可变数组alls:

vector< int > alls //可变数组,存储序列中出现的元素的值,因为序列中元素的个数非常小,所以这个代价是非常小的

alls即为出现在序列中的元素的所有可能的值,但是是一步步加进去的,所以可能重复,而且加进去的顺序是无序的、松散的,我们想要把它映射成紧凑的序列,应该对这alls进行排序、去重

sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素
//unique函数实现把不重复的元素放到前面,重复的元素放到后面,返回一个指向重复元素起始位置的尾部指针

经过排序去重后的alls,存储的是这个序列按从小到大顺序出现的元素值,且不含重复元素,从各个元素的值来看,它们依然是松散的,但是相邻元素已经被放到连续的数组空间,也即它们的存储是紧凑连续的,从数组下标的0开始,0,1,2,…,n-1
所以我们自然的能想到将序列中出现的元素值映射成他们对应的数组下标
那么这里的映射关系怎么实现呢?给出序列中一个元素的值,怎么去找到它对应的下标?
可以采用二分算法来实现:

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }   //r即为x在离散化后的数组下标
    return r + 1; // 映射到1, 2, ...n   这里不同题目情况不同,返回值因情况而定
}                  

离散化的本质:
将数字本身key映射为它在数组中的索引index(1 based),所以通过二分求索引(value -> index)是离散化的本质。


代码模板

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素
//unique函数实现把不重复的元素放到前面,重复的元素放到后面,返回一个指向重复元素起始位置的尾部指针
// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, 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; // 映射到1, 2, ...n
}

作者:yxc
链接:https://www.acwing.com/blog/content/277/
来源:AcWing

你可能感兴趣的:(基础算法模板总结,算法,数据结构)