【离散化+二维差分】
离散化:
由于x的范围是1-10^9,我们肯定不能存一个长度为10^9的数组,但是因为points的length只有5*10^4,这个是可以存下的,所以可以离散化把原来的稀疏x映射到稠密的区间内,具体做法为统计所有出现的x和l,然后按照从小到达的顺序排序,把数值对下标进行映射。
二维差分:
我们知道差分的前缀和可以用来表示这个下标的值,当对[l, r]区间+1时,需要在差分数组l处+1,r+1处-1。
对于二维差分数组如果想对[(x1, y1), (x2, y2)]整个区间的值都+1,那么首先需要在(x1, y1)处+1,但是对于点(x2 + 1, y1)也就是右下角的点他不在被加1的范围内,所以在这个点处要-1,同理(x1, y2 + 1)处也应该-1,但是我们发现此时(x2 + 1, y2 + 1)处的值变成了1 - 1 - 1了,所以需要在这个点处再+1才行。
这就是二维差分的精髓了:左下角和右上角+1,左上角和右下角-1
然后计算二维差分数组的前缀和即可还原原来的数组
class Solution {
// 离散化+差分 3:46 20
public int[] countRectangles(int[][] rectangles, int[][] points) {
int[][] rs = rectangles, ps = points;
int i = 0, j = 0, l = ps.length, x, y;
int[] ans = new int[l];
// 离散化
TreeSet<Integer> xs = new TreeSet(), ys = new TreeSet();
for(var r: rs){
xs.add(r[0]); ys.add(r[1]);
}
for(var p: ps){
xs.add(p[0]); ys.add(p[1]);
}
Map<Integer, Integer> mx = new HashMap(), my = new HashMap();
for(var tx: xs) mx.put(tx, ++i);
for(var ty: ys) my.put(ty, ++j);
int m = mx.size(), n = my.size();
int[][] a = new int[m + 2][n + 2];
// 差分
for(var r: rs){
x = mx.get(r[0]); y = my.get(r[1]);
a[1][1] += 1;
a[1][y + 1] -= 1;
a[x + 1][1] -= 1;
a[x + 1][y + 1] += 1;
}
// 前缀和
for(i = 1; i <= m; i++){
for(j = 1; j <= n; j++){
a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
}
}
// 查询
i = 0;
for(var p: ps){
x = mx.get(p[0]); y = my.get(p[1]);
ans[i++] = a[x][y];
}
return ans;
}
}
【扩展】如果事先不知道所有的points,也就是说不能直接对ps进行差分化,那应该怎么办呢
其实也不是很难,找到第一个大于等于这个点的双下标,输出这个点的值即可。
class Solution {
// 离散化+二维差分数组+ points不确定 4:16 40
public int bs(int target, int[] arr){
int left = 0, right = arr.length - 1;
while(left <= right){
int mid = (left + right) >>> 1;
if(arr[mid] >= target) right = mid - 1;
else left = mid + 1;
}
return left;
}
public int[] countRectangles(int[][] rectangles, int[][] points) {
int[][] rs = rectangles, ps = points;
int i = 0, j = 0;
// 只对rs进行映射
TreeSet<Integer> xs = new TreeSet<Integer>(), ys = new TreeSet<Integer>();
for(var r: rs){
xs.add(r[0]); ys.add(r[1]);
}
int[] ax = xs.stream().mapToInt(t->t).toArray(), ay = ys.stream().mapToInt(t->t).toArray();
Map<Integer, Integer> mx = new HashMap(), my = new HashMap();
for(var x: xs) mx.put(x, ++i);
for(var y: ys) my.put(y, ++j);
int m = mx.size(), n = my.size();
int[][] a = new int[m + 2][n + 2];
// 二维差分
for(var r: rs){
int x = mx.get(r[0]), y = my.get(r[1]);
a[1][1] += 1;
a[1][y + 1] -= 1;
a[x + 1][1] -= 1;
a[x + 1][y + 1] += 1;
}
// 二维前缀和
for(i = 1; i <= m; i++){
for(j = 1; j <= n; j++){
a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
}
}
// 二分找点
int[] ans = new int[ps.length];
i = 0;
for(var p: ps){
int x = p[0], y = p[1];
int ix = bs(x, ax), iy = bs(y, ay);
if(ix == m || iy == n) ans[i++] = 0;
else{
ans[i++] = a[mx.get(ax[ix])][my.get(ay[iy])];
}
}
return ans;
}
}