Java 线段树Segment Tree

版权声明:转载请随意! https://blog.csdn.net/qq_41723615/article/details/89356351

线段树也称为区间树。

1.线段树介绍:

对于有一类问题,我们关心的是线段(或者区间)。

线段树的应用:

1.最典型的线段树问题:区间染色:

m次操作后,我们可以看见多少种颜色?

m次操作后,我们可以在[i,j]区间内看见多少种颜色?

有一面墙,长度为n,每次选择一段儿墙进行染色。

之前被染色的部分,还能继续被下一次染色所覆盖。

再两次覆盖:

可以使用数组来解决从[i,j]区间内的染色问题。

2.经典问题:区间查询

查询一个区间[i,j]的最大值,最小值,或者区间数字和。

例如一个实质的问题:基于区间的统计查询

2019年注册用户中消费最高的用户?消费最少的用户?学习时间最长的用户?

由于时间是在不断改变的,所以线段树也是一种动态的数据结构。更新与查询。

线段树也是一种二叉树的结构。

2.线段树的实现思想

在数组A中:

以求和为例,将数组A转换成线段树:

此时叶子节点只有一个元素,不过该节点也是一个区间,区间长度为1。

查询4到7区间范围的元素:

查询2到6区间范围的元素:

我们只需要找到我们关心的那个区间对应的一个或者多个节点,而不需要对该区间的每一个节点都进行遍历。

3.线段树基础表示

上面给出的线段树是基于数组元素个数为8的情况下,根节点表是从索引0到7,来表示整个数组区间。

左节点为前半段区间,右节点为右半段区间。由于8比较特殊,所以线段树是满的,如果线段树的区间有10个元素,则二叉树的结构不为满了:

如果区间是奇数位,该位置的左右子节点的区间范围就不一样了:

对于线段树,不一定是一棵满的二叉树,也不一定是一棵完全二叉树,线段树一棵平衡二叉树,依然可以用数组来表示。

平衡二叉树:最大的深度与最小的深度相差只有1。

堆也是平衡二叉树。

我们可以将上面给出的线段树看成是满的二叉树,也就是把最后一层中未满的节点位置看成为null,用数组来表示。

如果区间有n个元素,数组表示需要有多少个节点?

对于此问题,我们需要将线段树看成是一个满的二叉树。

这样每一层的节点个数就有一定的规律了。

h层的满二叉树节点个数为:2^h - 1个节点(大约为2^h个)

区间浪费问题:

如果区间只有五个元素,最后一层的3和4需要找到区间的位置,用数组的形式存储,就要有意的浪费前面6个位置。

不过该情况可以忽略或者避免。用二分搜索树的存储结构来实现线段树,可以解决此类问题。

public class SegmentTree<E> {

    private E[] tree;
    private E[] data;

    public SegmentTree(E[] arr){

        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];
    }

    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;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41723615/article/details/89356351