在遇到某些题的时候,我们会遇到下标 \(x,y\) 范围较大(如\(10^6\))但点数较小(比如就 \(10^5\) 个)的情况。如果只有一个 \(x\) 的话我们会选择使用 map 或者 unordered_map 来解决,但是如果是二维,这就有些难办了。
pair 转化(自写 hash)
因为点实际上很少,只不过是下标的范围太大了,我们没必要真的开那么大的数组,只需要想办法给这个两个下标搞出一个唯一的标记即可。使用 pair 就可以达到这一点。
因此我们可以使用 pair 作为 map 的第一键值,从而实现二维下标的一维化。
优点是简洁,易写。
缺点是 unordered_map 无法使用 pair 作为第一键值,因为 unordered_map 本身没有 pair 的 hash 方法,因此如果想给 unordered_map 使用就只能自写 hash,但是这很困难、很麻烦,而如果不用 unordered_map 的话,一般情况下就只能使用 map,而 map 带有 \(\operatorname O(\log n)\) 的排序,而我们往往不需要这个,排序,这就会拖慢我们的程序。对于时间不友好。
总的看,这种办法不怎么好。
二维下标一维化
即使用进制的办法,把二维的下标转化为一维的一个数里面去。比如:如果 \(x,y \in [1, N]\),那么 \(id = x \times N + y\),通过这种方式,把两个 \(x,y\) 化成一个数字。通过这种方式,就可以用一维的 unordered_map 去存储二维下标,兼顾了时间和空间,而且简单。
缺点也是有的,就是这个 \(id\) 的范围不能太大,不能把 longlong 给爆掉(unorderd_map 无法使用 int128 作为第一键值)。因此如果这个 \(x,y\) 的范围不超过 \(10^9\) 的话,这种办法是很棒的。(一般的 \(x,y\) 范围也不会超过 \(10^9\),因此这个方法基本够用)
使用字符串 string 作为第一键值
这个方法和 pair 类似,但是好处的 unordered_map 可以使用,但相对的是使用不算方便,且效率相对较低(一般比二维下标一维化要慢上个小 \(10\) 倍)。
拼接字符串是很难受的,而且直接拼接会出现冲突情况。如 \(x = 24, y= 123\) 和 \(x = 242, y = 23\) 这两组在拼接后的字符串都是 \(24123\),因此不算是一种好办法。
map 嵌套
即把 map 的第二键值设为另一个 map,从而实现二维 map。
因为 map 依靠第一键值进行排序,所以第一键值必须可以对比大小,而当另一 map 在第二键值,就相当于当前 map 里面存的还是 map,这个 map 可以再次使用 []
进行索引,就可以像使用二维数组一样使用 map,如下:
map<int, map<int, int>> q;
q[x][y] = 1234;
这是一种真正的二维 map,通过这种方式,我们还可以实现三维、四维,或者 \(n\) 维。并且使用方便,对于下标完全没有范围限制,非常适合使用。
但是因为是基于 map(unordered_map 无法嵌套)所以在时间上会多出一个 \(\log n\),且嵌套 \(m\) 维,就需要多花 \(m \log n\) 的时间。因此在时间允许的情况下,可以使用这种方式,进行二维极大数组的存储。