segment树(线段树)
线段树(segment tree)是一种Binary Search Tree或者叫做ordered binary tree。对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。如下图:
[0-2]
/ \
[0-1] [2-2]
/ \
[0-0] [1-1]
下面看一道leetcode上的题,求动态区间的和(Range Sum Query - Mutable),题目如下:
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. The update(i, val) function modifies nums by updating the element at index i to val. Example: Given nums = [1, 3, 5] sumRange(0, 2) -> 9 update(1, 2) sumRange(0, 2) -> 8 Note: The array is only modifiable by the update function. You may assume the number of calls to update and sumRange function is distributed evenly
分析如下:
一、构造线段树节点:
class SegmentTreeNode { int start, end; int sum; SegmentTreeNode ltree, rtree; public SegmentTreeNode(int s, int e) { start = s; end = e; } }
二、建立线段树(根据数组nums,建立一个动态区间求和的线段树):
public SegmentTreeNode buildTree(int[] nums, int left, int right) { SegmentTreeNode root = new SegmentTreeNode(left, right); if (left == right) { root.sum = nums[left]; } else { int mid = left + (right - left)/2; root.ltree = buildTree(nums, left, mid); root.rtree = buildTree(nums, mid+1, right); root.sum = root.ltree.sum + root.rtree.sum; } return root; }
三、线段树的更新(更新int数组下标i的值为val):
private void update(SegmentTreeNode root, int i, int val) { if (root.start == root.end) { root.sum = val; } else { int mid = root.start + (root.end-root.start)/2; if (i <= mid) { update(root.ltree, i, val); } else { update(root.rtree, i, val); } root.sum = root.ltree.sum + root.rtree.sum; } }
四、线段树的查询(查询int数组下标 i 到 j 的元素之和):
private int sumRange(SegmentTreeNode root, int i, int j) { if (root.start == i && root.end == j) { return root.sum; } else { int mid = root.start + (root.end - root.start)/2; if (j <= mid) { return sumRange(root.ltree, i, j); } else if (i > mid) { return sumRange(root.rtree, i, j); } else { return sumRange(root.ltree, i, root.ltree.end) + sumRange(root.rtree, root.rtree.start, j); } } }
综上所述,上面Range Sum Query - Mutable的AC代码如下:
class SegmentTreeNode { int start, end; int sum; SegmentTreeNode ltree, rtree; public SegmentTreeNode(int s, int e) { start = s; end = e; } } public class NumArray { SegmentTreeNode root = null; public NumArray(int[] nums) { if(nums == null || nums.length == 0) { return; } root = buildTree(nums, 0, nums.length-1); } public SegmentTreeNode buildTree(int[] nums, int left, int right) { SegmentTreeNode root = new SegmentTreeNode(left, right); if (left == right) { root.sum = nums[left]; } else { int mid = left + (right - left)/2; root.ltree = buildTree(nums, left, mid); root.rtree = buildTree(nums, mid+1, right); root.sum = root.ltree.sum + root.rtree.sum; } return root; } void update(int i, int val) { update(root, i, val); } private void update(SegmentTreeNode root, int i, int val) { if (root.start == root.end) { root.sum = val; } else { int mid = root.start + (root.end-root.start)/2; if (i <= mid) { update(root.ltree, i, val); } else { update(root.rtree, i, val); } root.sum = root.ltree.sum + root.rtree.sum; } } public int sumRange(int i, int j) { return sumRange(root, i, j); } private int sumRange(SegmentTreeNode root, int i, int j) { if (root.start == i && root.end == j) { return root.sum; } else { int mid = root.start + (root.end - root.start)/2; if (j <= mid) { return sumRange(root.ltree, i, j); } else if (i > mid) { return sumRange(root.rtree, i, j); } else { return sumRange(root.ltree, i, root.ltree.end) + sumRange(root.rtree, root.rtree.start, j); } } } }