LeetCode 2250. 统计包含每个点的矩形数目

2250. 统计包含每个点的矩形数目

 

【离散化+二维差分】

离散化:

由于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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值