部分文章内容为公开资料查询整理,原文出处可能未标注,如有侵权,请联系我,谢谢。邮箱地址:gnivor@163.com ►►►需要气球么?请点击我吧!

数据结构--线段树(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));
}

 

 

posted @ 2015-11-19 16:15  流了个火  阅读(216)  评论(0)    收藏  举报
►►►需要气球么?请点击我吧!►►►
View My Stats