[] Data structure segment tree (Tree section)

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/qq_42322103/article/details/97154862

Article Directory

Outline

Segment tree is also known as "interval tree", it's also very common application scenarios, the focus is on "the line", or a range. Very classic tree line entitled "section stain", Soso see chant!

Sometimes the data we are dealing with, we also need to query interval, for example, the maximum value of a query interval [i, j], minimum, or range of numbers and. Into life scene is in the year 2019, your blog at what time the people following you fastest growing ah, the day his blog MOST READ period of time, ah, a total of celestial bodies in the galaxy and many more. This scenario can be a segment of the tree.


Bowen recommended two excellent
step by step to understand tree line
segment tree Detailed

Segment tree is not complete binary tree, but it is a balanced binary tree, still can be represented as an array. Do not be surprised, the heap is balanced binary tree form.

achieve

Implementation of an array of relatively simple point, where the use of an array of ways to show you.

First, we have before us is that if the interval has n elements, then expressed the need for an array of how many nodes?

Think full binary tree node regularity last layer is very strong,

The number of layers Nodes
Layer 0 1
1 story 2
2 layer 4
3 layers 8
n-1 layer 2 (n-1) th power

For a full binary tree: n layer, a total of 2 ^ n -1 nodes (approximately 2 ^ n)

The last layer (n-1 layer), there are 2 (n-1) th node
final layer of nodes is substantially equal to the sum of all preceding layers nodes

If there are n elements interval, k is the value of n-th power of 2, then requires only 2n space
value of n + 1 is the k-th power of 2, then this is the worst case requires space to 4n

Element range are on the leaf node.
This part must move to draw pen to paper to write all of a sudden be able to understand

That is 4n space will be able to put aside all the elements (not considering adding elements), and in fact our segment tree will not necessarily be full binary form here as a full binary tree in order to facilitate the process, not the full binary tree, then wasted space will be more, but to pick up some space for time is worth it.

A combination of the leaf nodes of the tree line to specific business scenarios, so create an interface specific implementation here is to see their own

public interface Merger<E> {
    E merge(E a, E b);
}

Create an implementation section below tree line query and update

public class SegmentTree<E> {

    private E[] tree;  //线段树数组
    private E[] data;  //存放传入进来的数组的副本
    private Merger<E> merger;  //data --> tree


    //************************************************
    //初始化和创建线段树
    //************************************************
    //构造函数中的两个参数 一个是数组 一个是合并的方案
    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, arr.length - 1);
    }

    // 在treeIndex的位置创建表示区间[left...right]的线段树
    private void buildSegmentTree(int treeIndex, int left, int right){

        if(left == right){
            tree[treeIndex] = data[left];
            return;
        }

        int leftTreeIndex = leftChild(treeIndex);  //获取左孩子的节点
        int rightTreeIndex = rightChild(treeIndex); //获取右孩子的节点

        // int mid = (left + right) / 2;
        int mid = left + (right - left) / 2;  //分支
        buildSegmentTree(leftTreeIndex, left, mid);
        buildSegmentTree(rightTreeIndex, mid + 1, right);

        //之所以用merge  是由具体的业务逻辑决定的 可以是加等 需要自己定义
        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;
    }



    //************************************************
    //查询
    //************************************************

    // 返回区间[queryL, queryR]的值
    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);
    }


    // 在以treeIndex为根的线段树中[l...r]的范围里,搜索区间[queryL...queryR]的值
    private E query(int treeIndex, int left, int right, int queryL, int queryR){

        if(left == queryL && right == queryR)
            return tree[treeIndex];

        int mid = left + (right - left) / 2;
        // treeIndex的节点分为[l...mid]和[mid+1...r]两部分

        int leftTreeIndex = leftChild(treeIndex);
        int rightTreeIndex = rightChild(treeIndex);
        //简化递归 只递归一边
        if(queryL >= mid + 1)
            return query(rightTreeIndex, mid + 1, right, queryL, queryR);
        else if(queryR <= mid)
            return query(leftTreeIndex, left, mid, queryL, queryR);
        //当无法简化递归时 两边都要递归
        E leftResult = query(leftTreeIndex, left, mid, queryL, mid);
        E rightResult = query(rightTreeIndex, mid + 1, right, 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 left, int right, int index, E e){

        if(left == right){
            tree[treeIndex] = e;
            return;
        }

        int mid = left + (right - left) / 2;
        // treeIndex的节点分为[l...mid]和[mid+1...r]两部分

        int leftTreeIndex = leftChild(treeIndex);
        int rightTreeIndex = rightChild(treeIndex);
        if(index >= mid + 1)
            set(rightTreeIndex, mid + 1, right, index, e);
        else // index <= mid
            set(leftTreeIndex, left, mid, index, e);

        tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
    }

    //************************************************
    //打印输出
    //************************************************
    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append('[');
        for(int i = 0 ; i < tree.length ; i ++){
            if(tree[i] != null)
                res.append(tree[i]);
            else
                res.append("null");

            if(i != tree.length - 1)
                res.append(", ");
        }
        res.append(']');
        return res.toString();
    }
}

Segment tree can also be extended to a two-dimensional tree line (matrix)
Here Insert Picture Description
as well as dynamic segment tree (using chain), to solve the waste of space, but also realize the number of elements of the left and right branches of custom.

There is another range of operations related to important data structure - Fenwick tree (Binary Index Tree), in addition to RMQ etc., we are interested can Soso chant!

Guess you like

Origin blog.csdn.net/qq_42322103/article/details/97154862