0    课程地址

https://coding.imooc.com/lesson/207.html#mid=13847

 

1    重点关注

1.1    线段树应用场景

如3.1和3.2对比,不可变的数组进行逻辑运算时,数组运行速度更快,但是用线段树也未尝不可

如3.3运行时间,当进行单元素更新操作时,数组的时间复杂度时O(n),而线段树是O(logn),如果是多个元素更新,那么差距更大。所以有更新操作的时候用线段树更快。

 

 

2    课程内容

 

 

3    Coding

3.1    leetCode303问题解析1(线段树实现)

  • 需求
给定一个整数数组  nums,处理以下类型的多个查询:

    计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和 ,其中 left <= right

实现 NumArray 类:

    NumArray(int[] nums) 使用数组 nums 初始化对象
    int sumRange(int i, int j) 返回数组 nums 中索引 left 和 right 之间的元素的 总和 ,包含 left 和 right 两点(也就是 nums[left] + nums[left + 1] + ... + nums[right] )

 

示例 1:

输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]

解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) 
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))

 

提示:

    1 <= nums.length <= 104
    -105 <= nums[i] <= 105
    0 <= i <= j < nums.length
    最多调用 104 次 sumRange 方法

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/range-sum-query-immutable

 

  • 代码实现
package com.company;

/**
 * 1    线段树解题
 * @author weidoudou
 * @date 2023/1/18 6:47
 **/
class NumArray {

    SegmentTree<Integer> segmentTree;

    public NumArray(int[] nums) {
        if(nums.length==0){
            throw new IllegalArgumentException("数组长度为0");
        }

        //装箱
        Integer[] numsInteger = new Integer[nums.length];
        for(int i = 0;i<nums.length;i++){
            numsInteger[i] = nums[i];
        }

        segmentTree = new SegmentTree<>(numsInteger,(a,b)->{return a+b;});

    }
    
    public int sumRange(int left, int right) {
        return segmentTree.query(left, right);
    }
}

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(left,right);
 */

 

  • 线段树类:
package com.company;

/**
 * 线段树
 * 根据入参数组的元素个数,制造出线段树节点元素个数
 * @author weidoudou
 * @date 2023/1/15 15:24
 **/
public class SegmentTree<E> {
    //1     初始化线段树元素和数组元素
    private E[] data;
    private E[] tree;
    private Merge<E> merger;

    /**
     * 初始化数组元素和tree(4*元素个数上节有推导)
     * @author weidoudou
     * @date 2023/1/15 15:34
     * @param nums 请添加参数描述
     * @return null
     **/
    public SegmentTree(E [] nums,Merge<E> merger){
        data = (E[])new Object[nums.length];
        for(int i = 0; i < nums.length; i++){
            data[i] = nums[i];
        }

        this.merger = merger;

        tree = (E[])new Object[4*nums.length];
        buildSegmentTree(0,0,data.length-1);
    }

    //2     创建线段树
    private void buildSegmentTree(int index,int l,int r){
        //2.1   终止条件
        if(l==r){
            tree[index] = data[l];

        //2.2   循环条件
        }else{
            //定义mid 将线段树节点平均拆分
            int mid = l+(r-l)/2;
            int indexL = getLeftChild(index);
            int indexR = getRightChild(index);
            buildSegmentTree(indexL,l,mid);
            buildSegmentTree(indexR,mid+1,r);
            tree[index] = merger.merge(tree[indexL],tree[indexR]);
        }

    }

    //3     查询线段树区间
    public E query(int queryL,int queryR){
        if(queryL<0||queryR>= data.length||queryL>queryR){
            throw new IllegalArgumentException("index is error");
        }
        return query(queryL,queryR,0,0, data.length-1);
    }

    private E query(int queryL,int queryR,int index,int l,int r){

        if(queryL == l&&queryR == r){
            return tree[index];
        }

        int mid = l+(r-l)/2;
        int leftIndex = getLeftChild(index);
        int rightIndex = getRightChild(index);

        //如果
        if(queryL>=mid+1){
            return query(queryL,queryR,rightIndex,mid+1,r);
        }else if(queryR<=mid){
            return query(queryL, queryR,leftIndex,l,mid);
        }else{
            E leftE = query(queryL, mid,leftIndex,l,mid);
            E rightE = query(mid+1,queryR,rightIndex,mid+1,r);
            return merger.merge(leftE, rightE);
        }
    }


    //4     基本方法
    public int getSize(){
        return data.length;
    }

    public E get(int index){
        if(index<0||index>=getSize()){
            throw new IllegalArgumentException("get失败");
        }
        return data[index];
    }

    public int getLeftChild(int index){
        return index*2+1;
    }

    public int getRightChild(int index){
        return index*2+2;
    }

    public String toString(){
        StringBuffer sbf = new StringBuffer();
        sbf.append("[");
        for(int i = 0;i< tree.length;i++){
            if(tree[i]!=null){
                sbf.append(tree[i]);
            }else{
                sbf.append("null");
            }
            if(i!=tree.length-1){
                sbf.append(",");
            }
        }


        sbf.append("]");
        return sbf.toString();
    }


}

 

  • 自定义接口
package com.company;

/**
 * 融合函数
 * @author weidoudou
 * @date 2023/1/17 6:38
 **/
public interface Merge<E> {
    E merge(E left,E right);
}

 

  • 测试结果

 

 

 

3.2    leetCode303问题解析2(数组实现)

  • 需求

如3.1

 

  • 代码实现
package com.company;

/**
 * 1    常规方法解题
 * @author weidoudou
 * @date 2023/1/18 6:47
 **/
class NumArray2 {

    int[] sumE;
    public NumArray2(int[] nums) {

        sumE = new int[nums.length+1];
        sumE[0] = 0;
        for(int i = 1;i<=nums.length;i++){
            sumE[i] = sumE[i-1]+nums[i-1];
        }
    }
    
    public int sumRange(int left, int right) {
        return sumE[right+1]-sumE[left];
    }
}

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(left,right);
 */

 

  • 测试结果

 

 

 

3.3    leetCode307问题解析

  • 需求
给你一个数组 nums ,请你完成两类查询。

    其中一类查询要求 更新 数组 nums 下标对应的值
    另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 ,其中 left <= right

实现 NumArray 类:

    NumArray(int[] nums) 用整数数组 nums 初始化对象
    void update(int index, int val) 将 nums[index] 的值 更新 为 val
    int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right])

 

示例 1:

输入:
["NumArray", "sumRange", "update", "sumRange"]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]

解释:
NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2);   // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8

 

提示:

    1 <= nums.length <= 3 * 104
    -100 <= nums[i] <= 100
    0 <= index < nums.length
    -100 <= val <= 100
    0 <= left <= right < nums.length
    调用 update 和 sumRange 方法次数不大于 3 * 104 

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/range-sum-query-mutable

 

  • 代码实现
package com.company;

class NumArray {

    int[] sumE;
    int[] data;
    public NumArray(int[] nums) {
        data = new int[nums.length];
        for(int i = 0;i<nums.length;i++){
            data[i] = nums[i];
        }

        sumE = new int[nums.length+1];
        sumE[0] = 0;
        for(int i = 1;i<=nums.length;i++){
            sumE[i] = sumE[i-1]+nums[i-1];
        }
    }
    
    public void update(int index, int val) {
        data[index] =  val;
        for(int i = index+1;i<=data.length;i++){
            sumE[i] = sumE[i-1]+data[i-1];
        }
    }
    
    public int sumRange(int left, int right) {
        return sumE[right+1]-sumE[left];
    }

}

 

  • 测试结果

 

posted on 2023-01-18 07:34  菜鸟乙  阅读(167)  评论(0编辑  收藏  举报