线段树--让区间求和和最值问题变简单

为什么使用线段树

最经典的线段树问题:PMQ(区间最值问题)和区间求和

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

线段树能帮助我们快速的求出区间和

构建线段树

1、线段树不是完全二叉树,是平衡二叉树。可以使用数组表示树的结点

2、给定搞一个数组arr,获取生成线段树的高度

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

3、生成线段树的结点个数最多为:2^h - 1

4、生成线段树

    private T[] source; // 源数组
    private T[] data; // 线段树对应的数组
    private Merger<T> merger;

    public SegmentTree(T[] arr, Merger<T> merger) {
        this.merger = merger;
        if (arr != null) {
            this.source = Arrays.copyOf(arr, arr.length);
            int height = (int) Math.ceil(Math.log(this.source.length) / Math.log(2) + 1);
            int dataLength = (int) Math.pow(2, height) - 1;
            this.data = (T[]) (new Object[dataLength]);
            buildingSegmentTree(0, this.source.length - 1, 0);
        }
    }
    /**
     * 构建线段树
     *
     * @param start 区间的开始
     * @param end   区间的结尾
     * @param index 线段树对应[start:end]区间的索引
     */
    private void buildingSegmentTree(int start, int end, int index) {
        //1、递归到底的情况
        if (start == end) {
            this.data[index] = this.source[start];
            return;
        }

        //2、递归操作
        int mid = start + (end - start) / 2;
        int leftIndex = 2 * index + 1;
        int rightIndex = leftIndex + 1;
        buildingSegmentTree(start, mid, leftIndex);
        buildingSegmentTree(mid + 1, end, rightIndex);
        this.data[index] = this.merger.merge(this.data[leftIndex], this.data[rightIndex]);
    }

注意:int mid = (start + end)/2;可能会超范围,所以使用 int mid = start +(end -start)/2 此构建线段树的方法只支持求和操作,为了具有通用性,可以设计一个融合接口Merge

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

查询操作

线段树的查询无非就是区间查询,我们只需要输入开始的区间和结束的区间,就可以获取这个区间你所需要的查询结果。

    public T getRangValue(int from, int to) {
        if (from < 0 || to >= this.source.length || from > to) {
            throw new IllegalArgumentException("error");
        }
        return query(0, this.source.length - 1, 0, from, to);
    }
    /**
     * 进行查询操作
     *
     * @param start 线段树结点所表示区间的start
     * @param end   线段树结点所表示区间的end
     * @param index 线段树上结点的索引
     * @param from  查区间的开询始
     * @param to    查询区间的结尾
     * @return
     */
    private T query(int start, int end, int index, int from, int to) {
        //1、递归到底的情况
        if (start == from && end == to){
            return this.data[index];
        }
        //2、递归操作  1)完全不包含  2)包含部分  3)包含全部
        int mid = start + (end - start) / 2;
        int leftIndex = 2 * index + 1;
        int rightIndex = leftIndex + 1;
        if (mid<from){
            return query(mid+1,end,rightIndex,from,to);
        }else if (to<=mid){
            return query(start,mid,leftIndex,from,to);
        }else {
            T leftVal = query(start,mid,leftIndex,from,mid);
            T rightVal = query(mid+1,end,rightIndex,mid+1,to);
            return this.merger.merge(leftVal,rightVal);
        }
    }

更新操作

给顶一个位置和元素,将位置中的元素替换成我们想要替换的元素,只需要在线段树中找到那个位置,然后替换即可,不过也需要将其他相关区间的值进行修改。

    public void update(int site,T val){
        if (site<0 || site > this.source.length){
            throw new IllegalArgumentException("site is wrongful!");
        }
        update(0,this.source.length-1,0,site,val);
    }

    private void update(int start, int end, int index, int site, T val) {
        //1、递归到底
        if (start == end && start == site){
            this.data[index] = this.source[site] = val;
            return;
        }
        int mid = start+ (end-start)/2;
        int leftIndex = index*2+1;
        int rightIndex = leftIndex+1;
        if (mid<site){
            update(mid+1,end,rightIndex,site,val);
        }else {
            update(start,mid,leftIndex,site,val);
        }
        this.data[index] = this.merger.merge(this.data[leftIndex],this.data[rightIndex]);
    }

练习

力扣303题

区域和检索-数组不可变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值