Segment Tree 的应用

最适合用 Segment tree 的情形最好同时满足以下三点:

  • 区间查找 min/max

  • 频繁 update

  • 频繁 query

Range Sum Query - Mutable

非常不错的一道题,Segment Tree的常用操作都考到了。

这题更快的做法是用 Binary Indexed Tree。

但是这道题不用BIT便可以run所有的testcase。后面的题有的就不行了。

public class RangeSumQueryMutable {
     public class NumArray {
         private class SegmentTreeNode {
             int start, end, sum;
             SegmentTreeNode left, right;
             public SegmentTreeNode() {}
             public SegmentTreeNode(int start, int end, int sum) {
                 this.start = start;
                 this.end = end;
                 this.sum = sum;
                 this.left = null;
                 this.right = null;
             }
         }

         SegmentTreeNode root;

         public NumArray(int[] nums) {
             root = buildTree(0, nums.length - 1, nums);
         }

         private SegmentTreeNode buildTree(int start, int end, int[] nums) {
             if (nums == null || start > end) {
                 return null;
             }
             if (start == end) {
                 return new SegmentTreeNode(start, end, nums[start]);
             }

             int mid = start + (end - start) / 2;

             SegmentTreeNode left = buildTree(start, mid, nums);
             SegmentTreeNode right = buildTree(mid + 1, end, nums);
             SegmentTreeNode root = new SegmentTreeNode(start, end, left.sum + right.sum);
             root.left = left;
             root.right = right;
             return root;
         }

         void update(int i, int val) {
             update(root, i, val);
         }

         private void update(SegmentTreeNode root, int index, int value) {
             if (root == null || index < root.start || index > root.end) {
                 return;
             }
             if (index == root.start && index == root.end) {
                 root.sum = value;
                 return;
             }
             update(root.left, index, value);
             update(root.right, index, value);
             root.sum = root.left.sum + root.right.sum;
         }

         public int sumRange(int i, int j) {
             return sumRange(root, i, j);
         }

         private int sumRange(SegmentTreeNode root, int start, int end) {
             if (root == null || start > end || start > root.end || end < root.start) {
                 return 0;
             }
             start = Math.max(start, root.start);
             end = Math.min(end, root.end);
             if (start == root.start && end == root.end) {
                 return root.sum;
             }
             int left = sumRange(root.left, start, end);
             int right = sumRange(root.right, start, end);
             return left + right;
         }
     }
}

Range Sum Query - Immutable

这题从类型上讲,看着和上一题非常像。然而其实这题因为需要的操作比较简单,其实就是一个 prefix sum 数组的 dp ...

教育了我们 segment tree 虽屌,也不要一言不合就随便用。。

  • 最适合用 Segment tree 的情形最好同时满足以下三点:

    • 区间查找 min/max

    • 频繁 update

    • 频繁 query

  • 在只有区间没有 update 的情况下,其实是一个一维/二维的 DP 问题,并不能体现出 segment tree 的优势。

  • 前缀和数组记得在最前面加上 sum = 0 的 padding.

public class NumArray {
     int[] prefix;
     public NumArray(int[] nums) {
         if (nums == null || nums.length == 0) {
             return;
         }
         prefix = new int[nums.length + 1];
         prefix[0] = 0;
         prefix[1] = nums[0];
         for (int i = 1; i < nums.length; ++i) {
             prefix[i + 1] = prefix[i] + nums[i];
         }
     }
     public int sumRange(int i, int j) {
         return prefix[j + 1] - prefix[i];
     }
}

Range Sum Query 2D - Mutable

这道题当然可以把矩阵降维之后用 segment tree 解,把一个 region 拆分成若干个 interval of rows 然后把结果加起来,但是很慢。

这题既体现了 segment tree的应用,又暴露了 segment tree的问题。

  • width = m, height = n, 现有 1D segment tree 的复杂度

    • build O(mn)

    • update O(log(mn))

    • query O(x * log (mn)) (x为所求矩形内的数的数量)

这题更适合用 binary index tree 解,segment tree的解法不贴了,因为在leetcode上面跑会超时。

results matching ""

    No results matching ""