其他分享
首页 > 其他分享> > 数据结构-线段树

数据结构-线段树

作者:互联网

数据结构-线段树

概述

线段树是一颗平衡的二叉搜索树,他以空间换区时间,让线性查找加速log级别的查找,用到的算法主要是二分搜索和递归。
例如:有数组data[]={1,2,3,4}, 我有一个需求,我需要频繁的查找区间i~j的sum和。这里先给出两个解决方案:

如果使用最普通的算法遍历,那么查找和更新的复杂度为O(n)。

当然你还可以使用动态规划,定义dp数组为dp[i][j] ,意义为从i~j的sum和,但是更新某个数据的复杂度也是O(n)。
又或者定义dp数组为dp[i], 意义为从0~i的sum和,同理更新的复杂度也是O(n)。

线段树图示
在这里插入图片描述
如果我们有办法构建图示这样的树,那么查找和更新都可以log(n)搞定。
首先我们考虑一个问题,构建这样的一棵树所需要的树节点个数与data的长度有什么关联,也就是说我的树节点应该开多少空间,第一棵树是一颗满二叉树,因为data正好是2的k次方,这样树的节点个数为2的k次方-1,最后一层的个数正好是上面所有层的节点个数+1,所有对于这种满二叉树节点个数开2倍data.length足够了,对于右边图,开4倍data.length足够了

源代码

buildSegmentTree图示
在这里插入图片描述

public class SegmentTree<E> {
    private E[] data;
    private E[] tree;
    private Merger<E> merger;

    public SegmentTree(E[] pData,Merger<E> pMerger){
        if(null==pData||pData.length==0)
            throw new RuntimeException("data must not empty");
        if(null==pMerger)
            throw new RuntimeException("merger must not empty");
        merger = pMerger;

        data = (E[]) new Object[pData.length];
        for(int i=0;i<pData.length;++i){
            data[i] = pData[i];
        }

        tree = (E[]) new Object[4*pData.length];
        buildSegmentTree(0,0,data.length-1);
    }

    //以treeIndex为根构建从left到right的线段树
    private void buildSegmentTree(int treeIndex, int left, int right) {
        if(left==right){
            tree[treeIndex] = data[left];
            return;
        }
        int mid = left+(right-left)/2;
        int leftIndex = leftIndex(treeIndex);
        int rightIndex = rightIndex(treeIndex);

        buildSegmentTree(leftIndex,left,mid);
        buildSegmentTree(rightIndex,mid+1,right);

        tree[treeIndex] = merger.merge(tree[leftIndex],tree[rightIndex]);
    }

    //查找
    public E query(int queryL,int queryR){
        if(queryL>queryR)
            throw new RuntimeException("queryL must < queryR");
        checkIndex(queryL);
        checkIndex(queryR);
        return query(0,queryL,queryR,0,data.length-1);
    }
    //以treeIndex为根从范围l-r查找queryL到queryR的线段树的merger值
    private E query(int treeIndex,int queryL,int queryR,int l,int r){
        //递归结束条件
        if(queryL==l&&queryR==r){
           return tree[treeIndex];
        }
        int mid = l+(r-l)/2;
        int leftIndex = leftIndex(treeIndex);
        int rightIndex = rightIndex(treeIndex);
        //左边
        if(queryR<=mid){
            return query(leftIndex,queryL,mid,l,mid);
        }
        //右边
        else if(queryL>mid){
            return query(rightIndex,mid+1,queryR,mid+1,r);
        }else{
            return merger.merge(query(leftIndex,queryL,mid,l,mid),query(rightIndex,mid+1,queryR,mid+1,r));
        }
    }

    //更新
    public void set(int index,E e){
        checkIndex(index);
        data[index] = e;

        set(0,0,data.length-1,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 leftIndex = leftIndex(treeIndex);
        int rightIndex = rightIndex(treeIndex);

        //右边更新
        if(index>mid){
            set(rightIndex,mid+1,r,index,e);
        }else{
            set(leftIndex,l,mid,index,e);
        }
        tree[treeIndex] = merger.merge(tree[leftIndex],tree[rightIndex]);
    }

    private void checkIndex(int index){
        if(index<0||index>=data.length){
            throw new RuntimeException("index is illegel");
        }
    }

    public int leftIndex(int index){
        return 2*index+1;
    }

    public int rightIndex(int index){
        return 2*index+2;
    }
}

public interface Merger<E> {

    E merge(E a,E b);

}
public class TestSegment {

    public static void main(String[] args) {
        Integer[] data = new Integer[]{1,2,3,4};
        SegmentTree<Integer> tree = new SegmentTree<>(data,(a,b)->{return a+b;});
        System.out.println(tree.query(1, 3));
        tree.set(2,10);
        System.out.println(tree.query(1, 3));
    }

}

标签:index,treeIndex,int,线段,tree,mid,数据结构,data
来源: https://blog.51cto.com/u_12856278/3042443