线段树【java实现】

一、解决问题

区间最值和区间求和问题

力扣相关题目: 

​​​​​​303. 区域和检索 - 数组不可变

729. 我的日程安排表 I

二、线段树定义

平衡二叉树,数组中的元素都存储在叶子结点中,如图是一个求区间最大值的线段树。

已知数组arr[11,5,3,7,8,2] ,树的高度与数组的长度关系为:

为了方便我们用代码实现线段树,将创建的数为满二叉树,如下图举例

三、实现线段树,并查询区间值

T[] data存储传入的原数组

T[] treeData存储构建的平衡二叉树

Merge<T> merge 融合器,表示线段树中节点值的计算方式

 Merge接口:

public interface Merge<T> {
    T merge(T v1,T v2);
}

线段树: 

import java.util.Arrays;

public class SegmentTree<T>{

    //原数组
    private T[] data;
    //构建的平衡二叉树的数组
    private T[] treeData;

    private Merge<T> merge;

    public SegmentTree(T[] arr,Merge<T> merge){
        // 入参判断
        if (arr==null||arr.length==0){
            return;
        }
        this.merge = merge;

        //将数组赋值到我们的原数组中
        this.data = Arrays.copyOf(arr,arr.length);

        //计算树的高度
        int h = (int)Math.ceil(Math.log10(arr.length)/Math.log10(2)+1);
        //平衡二叉树节点个数
        int length = (int)Math.pow(2,h)-1;
        //初始化树
        this.treeData = (T[]) new Object[length];
      //  构建平衡二叉树
        this.buildingSegmentTree(0,arr.length-1,0);
    }

    /**
     * 构建平衡二叉树
     * @param left 原数组的左边索引
     * @param right 原数组的右边索引
     * @param index 构建树的索引
     */
    private void buildingSegmentTree(int left,int right,int index){
    //   递归终止条件
        if (left==right){
            this.treeData[index] = this.data[left];
            return;
        }
    //    递归操作
        int mid = left+(right-left)/2;
        buildingSegmentTree(left,mid,2*index+1);
        buildingSegmentTree(mid+1,right,2*index+2);
        //计算当前节点的值
        this.treeData[index] = this.merge.merge(this.treeData[2*index+1],this.treeData[2*index+2]);
    }



    //查询
    public T query(int from,int to){
        if (from<0||to>this.data.length||to<from){
            return null;
        }
        return query(0,this.data.length-1,0,from,to);
    }
    /**
     *
     * @param left 原数组的左位置
     * @param right 原数组的右位置
     * @param index 平衡二叉树的中对应节点的索引
     * @param from 所求索引的开始位置
     * @param to 所求索引的结束位置
     * @return
     */
    private T query(int left,int right,int index,int from,int to){
        //找到区间
        if (left==from&&right==to){
            return this.treeData[index];
        }
        int mid = left + (right-left)/2;
        if (to<=mid){//去左找
            return query(left,mid,index*2+1,from,to);
        }else if (from>mid){//去右找
            return query(mid+1,right,index*2+2,from,to);
        }else {//两遍都有
            T leftVal = query(left,mid,index*2+1,from,mid);
            T rightVal = query(mid+1,right,index*2+2,mid+1,to);
            return this.merge.merge(leftVal,rightVal);
        }
    }

    public static void main(String[] args) {
        Integer[] arr = {1,3,5,7,9,11};
        SegmentTree<Integer> tree = new SegmentTree<Integer>(arr,(v1,v2)->v1>v2?v1:v2);
        int res =  tree.query(2,4);
        System.out.println(res);
    }
}

测试:

    public static void main(String[] args) {
        Integer[] arr = {11,5,3,6,7,8,2};
        SegmentTree<Integer> tree = new SegmentTree<Integer>(arr,(v1,v2)->v1>v2?v1:v2);
        int res =  tree.query(2,4);
        System.out.println(res);//7
    }

 

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小俱的一步步

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值