在一些场景或题目中,常常会遇到数据跨度较大的一堆数据。如果需要用连续空间存储,则会有大量空闲的空间,并且遍历的时间复杂取决于最大和最小值的间距。
如果不关系数据的具体大小,只关心数据的相对顺序,那么就可以用到本文重点离散化。
那么,什么是离散化?
离散化是指,将无限的数据,映射到有限的空间中并保留原来的全 / 偏序关系。
举个例子:
实现原理
以下以一维数组给出示例,将数据离散化到[0, n-1]的范围
例:
思路一:下标映射
如果将下标也一同排序,数据将是怎么的形式呢?
将下标和元素绑定后,有一个好处,对应每个元素能 O(1) 的找出该元素在原始数组中的位置。
因此,我们只需要顺序遍历排序后的元素,顺序的将原数组的值改为[0, n-1]的映射即可。
具体的我们可以如下操作:
排序后的第 0 号元素 —> 获取原数组 index 1 —> 将原数组的 1 号元素修改为 0 。
排序后的第 1 号元素 —> 获取原数组 index 4 —> 将原数组的 4 号元素修改为 1 。
排序后的第 2 号元素 —> 获取原数组 index 2 —> 将原数组的 2 号元素修改为 2 。
排序后的第 3 号元素 —> 获取原数组 index 3 —> 将原数组的 3 号元素修改为 3 。
排序后的第 4 号元素 —> 获取原数组 index 0 —> 将原数组的 0 号元素修改为 4 。
思路二:二分
其实这里的二分法回归本源也是基于下标映射的原理,只是实现是借助二分的形式。
在排序好的数组中对目标数值进行二分搜索,在 O(logn) 的时间复杂度内找到该数值是整体数据中的第几个。
具体的我们可以如下操作:
数值 10 —> 二分搜索 10 —> 有序序列中第 4 位置。
数值 3 —> 二分搜索 3 —> 有序序列中第 0 位置。
数值 8 —> 二分搜索 8 —> 有序序列中第 9 位置。
数值 9 —> 二分搜索 9 —> 有序序列中第 3 位置。
数值 4 —> 二分搜索 4 —> 有序序列中第 1 位置。
复杂度
时间复杂度:O(logn),主要体现在排序中,其余操作 <= O(logn) 。
空间复杂度:O(n),主要体现在用于排序的辅助数组中,不考虑 sort 中使用的空间。
代码
对下方代码的注释:
- 以 C++ 描述
- 以整形数据 int 描述
- 默认数据无重复值
- 接口名见下
/**
* @param odata 原始数据
* @param start 起始数值 (一般为0/1)
* @return std::vector<int> 经过离散化后的data
*/
vector<int> discrete(const vector<int>& odata, int start = 0) ;
排序 + 下标映射
sort 排序
vector<int> discrete(const vector<int>