一道线段树相关算法题

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

image.png


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


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


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


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


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


image.png


源:力扣(LeetCode)

链接:leetcode.cn/problems/fa…


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


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


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


为啥用线段树:线段树可以实现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;
    }
}


目录
相关文章
|
6月前
|
算法 Java C语言
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-8 算法训练 操作格子 线段树
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-8 算法训练 操作格子 线段树
55 0
|
6月前
|
算法 vr&ar
1611F - ATM and Students详细题解(*1800,线段树维护前缀和;双指针算法(思维))
1611F - ATM and Students详细题解(*1800,线段树维护前缀和;双指针算法(思维))
45 0
|
存储 人工智能 算法
【每日基础算法】线段树 - 树状数组
【每日基础算法】线段树 - 树状数组
161 0
【每日基础算法】线段树 - 树状数组
|
机器学习/深度学习 算法 Java
【每日算法】一题四解 : 「模拟」&「树状数组 (含去重优化) 」&「线段树」|Python 主题月
【每日算法】一题四解 : 「模拟」&「树状数组 (含去重优化) 」&「线段树」|Python 主题月
|
存储 移动开发 算法
【愚公系列】2021年11月 C#版 数据结构与算法解析(线段树)
【愚公系列】2021年11月 C#版 数据结构与算法解析(线段树)
134 0
【愚公系列】2021年11月 C#版 数据结构与算法解析(线段树)
|
人工智能 算法 BI
RMQ问题(线段树算法,ST算法优化)
RMQ (Range Minimum/Maximum Query)问题是指: 对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j
1555 0
|
算法
经典算法题每日演练——第十二题 线段树
原文:经典算法题每日演练——第十二题 线段树        这一篇我们来看树状数组的加强版线段树,树状数组能玩的线段树一样可以玩,而且能玩的更好,他们在区间求和,最大,平均 等经典的RMQ问题上有着对数时间的优越表现。
818 0
|
27天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。