线段树和Tire

本文介绍了线段树这一数据结构,用于解决区间最值问题和区间求和。线段树非完全二叉树,而是平衡二叉树,可以用数组表示。文章详细讲解了线段树的构建过程,包括确定高度、节点数量以及避免边界问题的方法。同时,提到了查询和更新操作,并提供了完整的代码实现。
摘要由CSDN通过智能技术生成

一、线段树能解决的问题

RMQ(区间最值问题)
区间求和

eg:如下图:区间求和[1:3] 答:42
区间[1:4]的最大值 答:22
在这里插入图片描述

二、构建线段树

(1)线段树不是完全二叉树,是平衡二叉树。可以使用数组表示树的节点
(2)给定一个数组arr ,获取生成线段树的高度
eg:2^(h-1) >=arr.length 的最小值
2^(h-1) >=8
h-1 >=log2^(8)
h>=4
所以生成线段树的高度为4
(3)节点个数最多为 : 2^(h) -1
(4)生成线段树

    /**
     * 构建线段树
     * @param start 区间的开始
     * @param end   区间的结尾
     * @param index 线段树对应的[start:end] 区间的索引
     */
    public 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 lefindex = 2 * index + 1;
        int rightindex = lefindex + 1;
        buildingSegmentTree(start, mid, lefindex);
        buildingSegmentTree(mid + 1, end, rightindex);
        this.data[index] = this.merger.merge(this.data[lefindex], 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);
}

    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  线段树节点所表示区间的start
     * @param end    线段树节点所表示区间的end
     * @param index  线段树上节点的索引
     * @param form   查询区间的开始
     * @param to     查询区间的结尾
     * @return
     */
    private   T query(int start,int end,int index,int form,int to){
        //1、递归到底的情况
        if (start == form&& end == to ){
            return this.data[index];
        }
        //2、递归操作 【1】完全不包含左边 【2】完全不包含右边 【3】左右两边都包含
        int leftIndex =2*index +1;
        int rightIndex =leftIndex+1;
        int mid =start +(end -start)/2;
        if (form>mid){
            return query(mid+1,end,rightIndex,form,to);
        }else  if (to<=mid){
            return query(start,mid,leftIndex,form,to);
        }else {
            //左边
            T leftValue = query(start,mid,leftIndex,form,mid);
            T rightValue = query(mid+1,end,rightIndex,mid+1,to);
            return  this.merger.merge(leftValue,rightValue);
        }

    }

在这里插入图片描述

四、更新操作

 /**
     * 更新源数组索引为index位置的值为val
     * @param site  源数组的索引
     * @param val   值
     */
    public  T  update(int site,T val){
        if (site<0||site>=this.source.length){
            throw  new IllegalArgumentException("index in invalid!");
        }
        //重新渲染线段树
     return    update(0,this.source.length-1,0,site,val);
    }

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

在这里插入图片描述

五、完整代码

package lesson7;

import java.util.Arrays;

public class SegmentTree<T> {
    public static void main(String[] args) {
        Integer[] arr = {19, 2, 18, 22, 5};
        SegmentTree<Integer> segmentTree = new SegmentTree<>(arr, (Integer a, Integer b) -> a + b);
        System.out.println(segmentTree);
        Integer result = segmentTree.getRangValue(1, 3);
        //索引从1-3的数组的和
        System.out.println("arr[1:3]的和=" + result);

        Integer integer= segmentTree.update(2,6);
        System.out.println("更新索引为2的值为6");
        System.out.println("arr数组最大值:"+integer);
        System.out.println(segmentTree);

    }
    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] 区间的索引
     */
    public 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 lefindex = 2 * index + 1;
        int rightindex = lefindex + 1;
        buildingSegmentTree(start, mid, lefindex);
        buildingSegmentTree(mid + 1, end, rightindex);
        this.data[index] = this.merger.merge(this.data[lefindex], this.data[rightindex]);
    }


    public T getRangValue(int form, int to) {
        if (form < 0 || to >= this.source.length || form > to) {
            throw new IllegalArgumentException("eorror");
        }
        return query(0, this.source.length - 1, 0, form, to);
    }

    /**
     * 进行查询操作
     * @param start  线段树节点所表示区间的start
     * @param end    线段树节点所表示区间的end
     * @param index  线段树上节点的索引
     * @param form   查询区间的开始
     * @param to     查询区间的结尾
     * @return
     */
    private   T query(int start,int end,int index,int form,int to){
        //1、递归到底的情况
        if (start == form&& end == to ){
            return this.data[index];
        }
        //2、递归操作 【1】完全不包含左边 【2】完全不包含右边 【3】左右两边都包含
        int leftIndex =2*index +1;
        int rightIndex =leftIndex+1;
        int mid =start +(end -start)/2;
        if (form>mid){
            return query(mid+1,end,rightIndex,form,to);
        }else  if (to<=mid){
            return query(start,mid,leftIndex,form,to);
        }else {
            //左边
            T leftValue = query(start,mid,leftIndex,form,mid);
            T rightValue = query(mid+1,end,rightIndex,mid+1,to);
            return  this.merger.merge(leftValue,rightValue);
        }

    }

    /**
     * 更新源数组索引为index位置的值为val
     * @param site  源数组的索引
     * @param val   值
     */
    public  T  update(int site,T val){
        if (site<0||site>=this.source.length){
            throw  new IllegalArgumentException("index in invalid!");
        }
        //重新渲染线段树
     return    update(0,this.source.length-1,0,site,val);
    }

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


    @Override
    public String toString() {
      StringBuilder sb =new StringBuilder("[");
      if (this.data!=null&&this.data.length!=0){
          Arrays.stream(this.data).forEach(item->{
              sb.append(item+",");
          });
      }
     /* sb.deleteCharAt(sb.length()-1);
      sb.append("]");
      return sb.toString();*/
        return sb.deleteCharAt(sb.length() - 1).append("]").toString();
    }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值