一道线段树相关算法题

在二维平面上的 x 轴上,放置着一些方块。

给你一个二维整数数组 positions ,其中 positions[i] = [lefti, sideLengthi] 表示:第 i 个方块边长为 sideLengthi ,其左侧边与 x 轴上坐标点 lefti 对齐。

每个方块都从一个比目前所有的落地方块更高的高度掉落而下。方块沿 y 轴负方向下落,直到着陆到 另一个正方形的顶边 或者是 x 轴上 。一个方块仅仅是擦过另一个方块的左侧边或右侧边不算着陆。一旦着陆,它就会固定在原地,无法移动。

在每个方块掉落后,你必须记录目前所有已经落稳的 方块堆叠的最高高度 。

返回一个整数数组 ans ,其中 ans[i] 表示在第 i 块方块掉落后堆叠的最高高度。

 来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/falling-squares

随机抽了个题,题目意思并不复杂,就是不断有方块掉落,后面掉的方块会压在前面掉的方块上面,要求返回每次落完方块后的最大高度。

额,虽然题目意思不难理解,但是……咳咳,我是废物,暴力的话感觉可能过不了(后面看评论区是能过的),然后就直接看大佬们写的题解咯

学了个 线段树 + 离散化 的方法感觉还不错,分享一下

为啥用线段树:线段树可以实现log级别的对某区间进行增加、修改、查询。

为啥离散化:采用4倍叶子节点的堆式线段树,题目说了 left <= 10 ^ 8 ,sideLength <= 10 ^ 6,这样所占空间太大了,会超出空间限制。而使用离散化并不会影响最终结果,所以使用了离散化

class Solution {
    static class SegmentTree {
        private int[] max;
        private int[] change;
        private boolean[] update;

        //4倍叶子节点的堆式线段树
        public SegmentTree(int size) {
            int N = size + 1;
            max = new int[N];
            //用于记录是否需要更新
            update = new boolean[N << 2];
            //用于记录如果要更新的话,要更新为啥
            change = new int[N << 2];
        }

        // 更新 rt 位置
        public void pushUp(int rt) {
            max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]);
        }

        // 更新 rt 下面的位置
        public void pushDown(int rt) {
            if (update[rt]) {
                update[rt] = false;
                update[rt << 1] = true;
                update[rt << 1 | 1] = true;
                change[rt << 1] = change[rt];
                change[rt << 1 | 1] = change[rt];
                max[rt << 1] = change[rt];
                max[rt << 1 | 1] = change[rt];
            }
        }

        // 更新 L~R 范围内的最大值
        public void update(int L, int R, int C, int l, int r, int rt) {
            if (L <= l && r <= R) {
                max[rt] = C;
                update[rt] = true;
                change[rt] = C;
                return;
            }
            int mid = (l + r) >> 1;
            pushDown(rt);
            if (L <= mid) {
                update(L, R, C, l, mid, rt << 1);
            }
            if (R > mid) {
                update(L, R, C, mid + 1, r, rt << 1 | 1);
            }
            pushUp(rt);
        }

        // 查询 L~R 范围内的最大值
        public int query(int L, int R, int l, int r, int rt) {
            if (L <= l && r <= R) {
                return max[rt];
            }
            int mid = (l + r) >> 1;
            pushDown(rt);
            int max = Integer.MIN_VALUE;
            if (L <= mid) {
                max = Math.max(max, query(L, R, l, mid, rt << 1));
            }
            if (R > mid) {
                max = Math.max(max, query(L, R, mid + 1, r, rt << 1 | 1));
            }
            return max;
        }
    }

    // 离散化
    public HashMap<Integer, Integer> index(int[][] positions) {
        TreeSet<Integer> set = new TreeSet<>();
        for (int[] position : positions) {
            set.add(position[0]);
            // 减 1 是为了避免一个方块的尾位置和另一个方块的头位置粘着时不使得相邻的点位置为两个方块的累加和
            // 所以统一每个方块右边位置减1
            set.add(position[0] + position[1] - 1); 
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        int index = 0;
        for (Integer num : set) {
            map.put(num, ++index);
        }
        return map;
    }

    public List<Integer> fallingSquares(int[][] positions) {
        HashMap<Integer, Integer> map = index(positions); //离散化
        int N = map.size();
        SegmentTree segmentTree = new SegmentTree(N); //创建线段树
        ArrayList<Integer> res = new ArrayList<>(); // 存储最终答案
        int max = Integer.MIN_VALUE; // 记录方块每次落下时的最大值
        for (int[] position : positions) { // 方块逐渐落下,线段树随之更新
            Integer L = map.get(position[0]);
            Integer R = map.get(position[0] + position[1] - 1);
            int height = segmentTree.query(L, R, 1, N, 1) + position[1]; // 落下当前方块后 L~R 处的高度
            max = Math.max(max, height);
            res.add(max);
            segmentTree.update(L, R, height, 1, N, 1);
        }
        return res;
    }
}

这题做下来收获还是很多的,希望大家也能有所收获呀

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 29
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值