美文网首页线段树
线段树(区间树)

线段树(区间树)

作者: 不服输的小蜗牛 | 来源:发表于2020-06-28 22:28 被阅读0次

    线段树:线段树是一种二叉树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
    线段树适用于不变的数据中获取某个区间的值,比如说获取2019年全年收入最高的一个月,由于当前是2020年所以2019年的数据是不变的,我们就可以通过线段树来完成。

    线段树是一个满二叉树,开辟的空间是4n个空间大小。
    对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

    代码实现

    
    public class SegmentTree<E> {
        private E[] tree;
        private E[] data;
        private Merger<E> merger;
    
        public SegmentTree(E[] arr, Merger<E> merger) {
    
            this.merger = merger;
    
            data = (E[]) new Object[arr.length];
            for (int i = 0; i < arr.length; i++) {
                data[i] = arr[i];
            }
            tree = (E[]) new Object[4 * arr.length];
            buildSegmentTree(0, 0, data.length - 1);
        }
    
    
        //在treeIndex的位置创建表示区间[l...r]的线段树,treeIndex的线段树是由左右两个孩子的合集
        private void buildSegmentTree(int treeIndex, int l, int r) {
    
            //终止条件,如果l==r 表示区间为1,是一个叶子节点。
            if (l == r) {
                tree[treeIndex] = data[l];
                return;
            }
    
            int leftTreeIndex = leftChild(treeIndex);
            int rightTreeIndex = rightChild(treeIndex);
    
            int mid = l + (r - l) / 2;
    
            //左孩子的区间是l...mid
            //右孩子的区间是mid+1...r
            buildSegmentTree(leftTreeIndex, l, mid);
            buildSegmentTree(rightTreeIndex, mid + 1, r);
            tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
        }
    
    
        public E query(int queryL, int queryR) {
            if (queryL < 0 || queryL >= data.length || queryR < 0 || queryR >= data.length || queryL > queryR) {
                throw new IllegalArgumentException("Index is illegal");
            }
            return query(0, 0, data.length - 1, queryL, queryR);
        }
    
        //在treeId为根的线段树中[l...r]范围里,搜索区间[queryL...queryR]的值。
        private E query(int treeIndex, int l, int r, int queryL, int queryR) {
    
            if (l == queryL && r == queryR) {
                return tree[treeIndex];
            }
    
            int mid = l + (r - l) / 2;
    
            int leftTreeIndex = leftChild(treeIndex);
            int rightTreeIndex = rightChild(treeIndex);
    
            if (queryL >= mid + 1) {
                return query(rightTreeIndex, mid + 1, r, queryL, queryR);
            } else if (queryR <= mid) {
                return query(leftTreeIndex, l, mid, queryL, queryR);
            }
    
            E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
            E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
            return merger.merge(leftResult, rightResult);
        }
    
    
        public void set(int index,E e){
            if(index <0 || index >=data.length){
                throw new IllegalArgumentException("Index is illegal");
            }
    
            data[index] = e;
            set(0,0,data.length-1,index ,e);
        }
    
        //在以treeIndex 为根的线段树中更新index的值为e
        private void set(int treeIndex,int l ,int r,int index, E e){
    
            if(l==r){
                tree[treeIndex] = e;
                return;
            }
    
    
            int mid = l +(r-l)/2;
            int leftTreeIndex = leftChild(treeIndex);
            int rightTreeIndex = rightChild(treeIndex);
            if(index >= mid+1){
                set(rightTreeIndex,mid+1,r,index,e);
            }else{
                set(leftTreeIndex,l,mid,index,e);
            }
            tree[treeIndex] = merger.merge(tree[leftTreeIndex],tree[rightTreeIndex]);
        }
    
        public int getSize() {
            return data.length;
        }
    
        public E get(int index) {
            if (index < 0 || index >= data.length) {
                throw new IllegalArgumentException("Index is illegal.");
            }
            return data[index];
        }
    
        //返回完全二叉树的数组表示中,一个索引锁表示的元素的左孩子节点的索引
        private int leftChild(int index) {
            return 2 * index + 1;
        }
    
        //返回完全二叉树的数组表示中,一个索引锁表示的元素的右孩子节点的索引
        private int rightChild(int index) {
            return 2 * index + 2;
        }
    }
    
    
    

    相关文章

      网友评论

        本文标题:线段树(区间树)

        本文链接:https://www.haomeiwen.com/subject/mlxgfktx.html