数据结构--线段树(segment tree)
segment tree这种数据结构主要应用在计算几何和地理信息系统中
树:是一棵树,而且是一棵二叉树。
线段:树上的每个节点对应于一个线段(还是叫“区间”更容易理解,区间的起点和终点通常为整数)
同一层的节点所代表的区间,相互不会重叠。
叶子节点的区间是单位长度,不能再分了。
下图是一个线段树

线段树是一棵二叉树,树中的每一个结点表示了一个区间[a,b]。a,b通常是整数。每一个叶子节点表示了一个单位区间。对于每一个非叶结点所表示的结点[a,b],其左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2,b](除法去尾取整)。
可由下面的例子了解线段树的使用
leetcode307 Range Sum Query
给定一个整数数组,找出下标位于i和j之间所有元素的和(含i和j)
使用两个函数:update(i,val) 更新下标为i的元素值为val;sumRange(i,j)求i到j的和
Example:
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
代码:
SegmentTree数据结构:
//线段树的数据结构 class SegmentTreeNode { int start, end; SegmentTreeNode left, right; int sum; public SegmentTreeNode(int start, int end) { this.start = start; this.end = end; this.left = null; this.right = null; this.sum = 0; } }
NumArray
//利用线段树 segment Tree public class NumArray { SegmentTreeNode root = null; public NumArray(int[] nums) { root = buildTree(nums, 0, nums.length - 1); } void update(int i, int val) { updateTree(root, i, val); } public int sumRange(int i, int j) { return sumRangeTree(root, i, j); } //--------------------------------------------------// //构建线段树 private SegmentTreeNode buildTree(int[] nums, int start, int end) { if (start > end) { return null; } else { SegmentTreeNode ret = new SegmentTreeNode(start, end); if (start == end) { //如果只有一个数字 ret.sum = nums[start]; } else { int mid = start + (end - start) / 2; ret.left = buildTree(nums, start, mid); ret.right = buildTree(nums, mid + 1, end); ret.sum = ret.left.sum + ret.right.sum; } return ret; } } // 更新线段树节点 void updateTree(SegmentTreeNode root, int pos, int val) { if (root.start == root.end) { //这个叶子节点的pos = start = end root.sum = val; } else { int mid = root.start + (root.end - root.start) / 2; if (pos <= mid) { updateTree(root.left, pos, val); } else { updateTree(root.right, pos, val); } root.sum = root.left.sum + root.right.sum; //需要更新路径上的和 } } //在指定线段树上找到start到end public int sumRangeTree(SegmentTreeNode root, int start, int end) { if (root.end == end && root.start == start) { //如果起点 终点是需要的起点和终点,root.sum为结果 return root.sum; } else { int mid = root.start + (root.end - root.start) / 2; if (end <= mid) { //如果需要的终点<=mid,则要求和的部分一定在左子树 return sumRangeTree(root.left, start, end); } else if (start >= mid + 1) { //如果需要的起点大于mid,则要求和的部分一定在右子树 return sumRangeTree(root.right, start, end); } else { //否则,求和的部分左右两侧都有,需要进下一层分别求 return sumRangeTree(root.left, start, mid) +sumRangeTree(root.right, mid + 1, end); } } } }
main函数
public static void main(String args[]){ int[] nums = {1, 3, 5}; NumArray2 n = new NumArray2(nums); System.out.println(n.sumRange(0, 2)); n.update(1, 2); System.out.println(n.sumRange(0, 2)); }

浙公网安备 33010602011771号