提到树状数组的具体应用就不得不提起离散化
什么是离散化呢?
很多时候,我们并不关心数组中每个值的大小,只关心它们的序的关系。1. 在求数组的逆序数的时候,98765 和 54321 具有相同的逆序数
2. 求数组的每个数右边有多少个更小的数
通常我们把 一个具有 n 个 unique values 的数组映射到 range [0, n)的整数的操作叫做离散化。
在使用树状数组解决上述问题的时候就必须要用到离散化!!!
以第二个问题为例,如果我们有一个离散化好的数组[3, 1, 0, 2],从右往左读。
第一步,初始化一个全0的、数组长度为4的树状数组类 (zero-indexed)
第二步,在树状数组的位置 2 加上1 (因为2是[1,0,2]的最后一个)
第三步,在树状数组的位置 0 加上1 (此处其实省略了 query)
第四步,在树状数组的位置 1 加上1,并计算(query)树状数组位置 1 之前的和(这个和是1),这个和就是数字1的右边比1小的数的个数!
第五步,在树状数组的位置 3 加上1,并计算(query)树状数组位置 3 之前的和(这个和是3),这个和就是数字3的右边比3小的数的个数!
相信你已经对离散化的作用有一定的了解了。那么,我们如何高效的实现对一个数组/序列的离散化呢??离散化需要的时间又是多少呢??
关于离散化,其实还有一个需要考虑的,就是数组有无重复元素,重复元素在离散化后的数组也需要具有相同的值。如果没有重复元素,程序会变得简单一些。以下都按数组有重复元素来考虑。
离散化方法一:排序后使用哈希映射
def discretization(l: list) -> list:
res = []
s = sorted(set(l))
d = {x:i for i, x in enumerate(s)}
for a in l:
res.append(d[a])
return res
这种方式的时间复杂度出在排序上,空间上需要维护一个哈希表
离散化方法二:排序后使用二分查找
import bisect
def discretization(l: list) -> list:
res = []
s = sorted(set(l))
for a in l:
res.append(bisect.bisect_left(s, a) + 1)
return res
这种方法的好处在于不需要一个哈希表了,但是二分查找需要更多的时间;空间更省但是时间更多。
关于二分查找,python有bisect标准库,C++的algorithm提供了lower_bound和upper_bound函数,Java有Arrays.binarySearch函数。