Java&C++题解与拓展——leetcode699.掉落的方块【线段树】

129 阅读3分钟
每日一题做题记录,参考官方和三叶的题解

题目要求

在这里插入图片描述

思路一:枚举模拟

Java

class Solution {
    public List<Integer> fallingSquares(int[][] positions) {
        int n = positions.length;
        List<Integer> height = new ArrayList<Integer>();
        for(int i = 0; i < n; i++) {
            int lc = positions[i][0], rc = positions[i][0] + positions[i][1] - 1; // 当前
            int res = positions[i][1];
            for(int j = 0; j < i; j++) {
                int lp = positions[j][0], rp = positions[j][0] + positions[j][1] - 1; // 之前的
                if(rc >= lp && rp >= lc)
                    res = Math.max(res, height.get(j) + positions[i][1]);
            }
            height.add(res);
        }
        for(int i = 1; i < n; i++)
            height.set(i, Math.max(height.get(i), height.get(i - 1)));
        return height;
    }
}
  • 时间复杂度:O(n2)O(n^2)
  • 空间复杂度:O(1)O(1)

C++

class Solution {
public:
    vector<int> fallingSquares(vector<vector<int>>& positions) {
        int n = positions.size();
        vector<int> height;
        for(int i = 0; i < n; i++) {
            int lc = positions[i][0], rc = positions[i][0] + positions[i][1] - 1; // 当前
            int res = positions[i][1];
            for(int j = 0; j < i; j++) {
                int lp = positions[j][0], rp = positions[j][0] + positions[j][1] - 1; // 之前的
                if(rc >= lp && rp >= lc)
                    res = max(res, height[j] + positions[i][1]);
            }
            height.push_back(res);
        }
        for(int i = 1; i < n; i++)
            height[i] = max(height[i], height[i - 1]);
        return height;
    }
};
  • 时间复杂度:O(n2)O(n^2)
  • 空间复杂度:O(1)O(1)

思路二:线段树【动态开点】

  • 可以把问题归结为『区间修改+区间查询』:
    • 当前方块左端点为lclc,则其右端点rc=lc+lenrc=lc+len
    • 题目即为查询与更新当前范围的最大高度;
    • 所以使用带”懒标记“的线段树

二.1、估点

  • 按照题设,可估算为6×m×logn6\times m\times\log n,其中mm指查询次数,在本题中为postionpostion数组的长度。

Java

class Solution {
    class Node {
        int ls, rs; // 当前左右节点在tree中下标
        int height, lazy;
    }
    int N = (int)1e9, cnt = 0;
    Node[] tr = new Node[1000010]; // 初始化一个超级无敌大数组
    void update(int u, int lc, int rc, int l, int r, int v) {
        if(l <= lc && rc <= r) {
            tr[u].height = v;
            tr[u].lazy = v;
            return ;
        }
        pushdown(u);
        int m = lc + rc >> 1;
        if(l <= m)
            update(tr[u].ls, lc, m, l, r, v);
        if(r > m)
            update(tr[u].rs, m + 1, rc, l, r, v);
        pushup(u);
    }
    int query(int u, int lc, int rc, int l, int r) {
        if(l <= lc && rc <= r)
            return tr[u].height;
        pushdown(u);
        int m = lc + rc >> 1, res = 0;
        if(l <= m)
            res = query(tr[u].ls, lc, m, l, r);
        if(r > m)
            res = Math.max(res, query(tr[u].rs, m + 1, rc, l, r));
        return res;
    }
    void pushdown(int u) {
        if(tr[u] == null)
            tr[u] = new Node();
        if(tr[u].ls == 0) {
            tr[u].ls = ++cnt;
            tr[tr[u].ls] = new Node();
        }
        if(tr[u].rs == 0) {
            tr[u].rs = ++ cnt;
            tr[tr[u].rs] = new Node();
        }
        if(tr[u].lazy == 0)
            return ;
        int lazy = tr[u].lazy;
        tr[tr[u].ls].lazy = lazy;
        tr[tr[u].rs].lazy = lazy;
        tr[tr[u].ls].height = lazy;
        tr[tr[u].rs].height = lazy;
        tr[u].lazy = 0;
    }
    void pushup(int u) {
        tr[u].height = Math.max(tr[tr[u].ls].height, tr[tr[u].rs].height);
    }

    public List<Integer> fallingSquares(int[][] positions) {
        List<Integer> res = new ArrayList<>();
        tr[1] = new Node();
        for(int[] p : positions) {
            int x = p[0], h = p[1];
            int cur = query(1, 1, N, x, x + h - 1);
            update(1, 1, N, x, x + h - 1, cur + h);
            res.add(tr[1].height);
        }
        return res;
    }
}
  • 时间复杂度:O(mlogn)O(m\log n),其中mm为数组长度,nn为值域大小
  • 空间复杂度:O(mlogn)O(m\log n)

C++

static int N = 1e9;
class Solution {
    int cnt = 0;
public:
    class Node {
    public: 
        int ls, rs; // 当前左右节点在tree中下标
        int height, lazy;
    };
    vector<Node*> tr = vector<Node*>(1000010); // 初始化一个超级无敌大数组
    void update(int u, int lc, int rc, int l, int r, int v) {
        if(l <= lc && rc <= r) {
            tr[u]->height = v;
            tr[u]->lazy = v;
            return ;
        }
        pushdown(u);
        int m = (lc + rc) >> 1;
        if(l <= m)
            update(tr[u]->ls, lc, m, l, r, v);
        if(r > m)
            update(tr[u]->rs, m + 1, rc, l, r, v);
        pushup(u);
    }
    int query(int u, int lc, int rc, int l, int r) {
        if(l <= lc && rc <= r)
            return tr[u]->height;
        pushdown(u);
        int m = (lc + rc) >> 1, res = 0;
        if(l <= m)
            res = query(tr[u]->ls, lc, m, l, r);
        if(r > m)
            res = max(res, query(tr[u]->rs, m + 1, rc, l, r));
        return res;
    }
    void pushdown(int u) {
        if(tr[u] == nullptr)
            tr[u] = new Node();
        if(tr[u]->ls == 0) {
            tr[u]->ls = ++cnt;
            tr[tr[u]->ls] = new Node();
        }
        if(tr[u]->rs == 0) {
            tr[u]->rs = ++ cnt;
            tr[tr[u]->rs] = new Node();
        }
        if(tr[u]->lazy == 0)
            return ;
        int lazy = tr[u]->lazy;
        tr[tr[u]->ls]->lazy = lazy;
        tr[tr[u]->rs]->lazy = lazy;
        tr[tr[u]->ls]->height = lazy;
        tr[tr[u]->rs]->height = lazy;
        tr[u]->lazy = 0;
    }
    void pushup(int u) {
        tr[u]->height = max(tr[tr[u]->ls]->height, tr[tr[u]->rs]->height);
    }

    vector<int> fallingSquares(vector<vector<int>>& positions) {
        vector<int> res;
        tr[1] = new Node();
        for(auto p : positions) {
            int x = p[0], h = p[1];
            int cur = query(1, 1, N, x, x + h - 1);
            update(1, 1, N, x, x + h - 1, cur + h);
            res.push_back(tr[1]->height);
        }
        return res;
    }
};
  • 时间复杂度:O(mlogn)O(m\log n),其中mm为查询次数,nn为值域大小
  • 空间复杂度:O(mlogn)O(m\log n)

二.2、动态指针

  • 可以有效避免new大数组的初始化开销,在不考虑static优化or全局数组优化的情况下,动态指针的方法更好一点。

Java

class Solution {
    class Node {
        Node ls, rs; // 当前左右节点在tree中下标
        int height, lazy;
    }
    int N = (int)1e9;
    Node root = new Node(); // 初始化一个根
    void update(Node node, int lc, int rc, int l, int r, int v) {
        if(l <= lc && rc <= r) {
            node.height = v;
            node.lazy = v;
            return ;
        }
        pushdown(node);
        int m = lc + rc >> 1;
        if(l <= m)
            update(node.ls, lc, m, l, r, v);
        if(r > m)
            update(node.rs, m + 1, rc, l, r, v);
        pushup(node);
    }
    int query(Node node, int lc, int rc, int l, int r) {
        if(l <= lc && rc <= r)
            return node.height;
        pushdown(node);
        int m = lc + rc >> 1, res = 0;
        if(l <= m)
            res = query(node.ls, lc, m, l, r);
        if(r > m)
            res = Math.max(res, query(node.rs, m + 1, rc, l, r));
        return res;
    }
    void pushdown(Node node) {
        if(node.ls == null)
            node.ls = new Node();
        if(node.rs == null)
            node.rs = new Node();
        if(node.lazy == 0)
            return ;
        int lazy = node.lazy;
        node.ls.lazy = lazy;
        node.rs.lazy = lazy;
        node.ls.height = lazy;
        node.rs.height = lazy;
        node.lazy = 0;
    }
    void pushup(Node node) {
        node.height = Math.max(node.ls.height, node.rs.height);
    }

    public List<Integer> fallingSquares(int[][] positions) {
        List<Integer> res = new ArrayList<>();
        for(int[] p : positions) {
            int x = p[0], h = p[1];
            int cur = query(root, 0, N, x, x + h - 1);
            update(root, 0, N, x, x + h - 1, cur + h);
            res.add(root.height);
        }
        return res;
    }
}
  • 时间复杂度:O(mlogn)O(m\log n),其中mm为查询次数,nn为值域大小
  • 空间复杂度:O(mlogn)O(m\log n)

C++

【遇事不决加个*号,解决一半报错】

static int N = 1e9;
class Solution {
public:
    class Node {
    public: 
        Node *ls, *rs; // 当前左右节点在tree中下标
        int height, lazy;
    };
    Node *root = new Node(); // 初始化一个超级无敌大数组
    void update(Node *node, int lc, int rc, int l, int r, int v) {
        if(l <= lc && rc <= r) {
            node->height = v;
            node->lazy = v;
            return ;
        }
        pushdown(node);
        int m = (lc + rc) >> 1;
        if(l <= m)
            update(node->ls, lc, m, l, r, v);
        if(r > m)
            update(node->rs, m + 1, rc, l, r, v);
        pushup(node);
    }
    int query(Node *node, int lc, int rc, int l, int r) {
        if(l <= lc && rc <= r)
            return node->height;
        pushdown(node);
        int m = (lc + rc) >> 1, res = 0;
        if(l <= m)
            res = query(node->ls, lc, m, l, r);
        if(r > m)
            res = max(res, query(node->rs, m + 1, rc, l, r));
        return res;
    }
    void pushdown(Node *node) {
        if(node->ls == nullptr)
            node->ls =  new Node();
        if(node->rs == nullptr)
            node->rs = new Node();
        if(node->lazy == 0)
            return ;
        int lazy = node->lazy;
        node->ls->lazy = lazy;
        node->rs->lazy = lazy;
        node->ls->height = lazy;
        node->rs->height = lazy;
        node->lazy = 0;
    }
    void pushup(Node *node) {
        node->height = max(node->ls->height, node->rs->height);
    }

    vector<int> fallingSquares(vector<vector<int>>& positions) {
        vector<int> res;
        for(auto p : positions) {
            int x = p[0], h = p[1];
            int cur = query(root, 0, N, x, x + h - 1);
            update(root, 0, N, x, x + h - 1, cur + h);
            res.push_back(root->height);
        }
        return res;
    }
};
  • 时间复杂度:O(mlogn)O(m\log n),其中mm为查询次数,nn为值域大小
  • 空间复杂度:O(mlogn)O(m\log n)

总结

学了一波线段树动态开点的巧妙应用,感觉可以单开一篇专门学习。因为不太会估点(数据结构学得不太行),所以我比较喜欢动态指针,简单快乐一点(虽然C++的指针又搞了半天)。

【哦这几天这么忙结果总是大难题……能不能平均一点】


欢迎指正与讨论!