将区间内的每个数都加X,获取累加后的值

搞了一个类,为了方便对区间内的每个数都加X,它有两种操作

  1. 对区间[a,b]里的每个数加x
  2. 获取位置i累加后的值

在这里插入图片描述

/**
 * 2021-03-15 一个用数组存的完全二叉树,可以对区间[0,i]里的每个数加x,便于获取位置i累加后的和,
 */
class MySumTree{
    constructor(len){
        // 叶子节点的个数,得是2的n次方
        this.leafLen = 1 << Math.ceil(Math.log2(len)); 
        // 数组的长度是子叶的二倍,arr[0]没用,arr[1]是根
        this.arr = new Array(this.leafLen << 1).fill(0);
    }
    // 对区间[0,i]里的每个数加x
    addLeft(i, x){
        if(i < 0) return;
        if(i >= this.leafLen-1){ // 全都加
            this.arr[1] += x;
        } else {
            i += this.leafLen; // 找到叶子节点
            for( ; i; i>>>=1){
                if((i&1) === 0){
                    this.arr[i] += x;
                    i--;
                }
            }
        }
    }
    // 对区间[i,len]里的每个数加x, 等价于全部加x,然后[0,i-1]加-x
    addRight(i, x){
        if(i >= this.leafLen) return;
        this.arr[1] += x;
        if(i-1 >= 0) this.addLeft(i-1, -x);
    }
    // 对区间[a,b]里的每个数加x,等价于[0,b]加x,然后[0,a-1]加-x
    addAB(a, b, x){
        if(b >= this.leafLen) return;
        if(b >= 0) this.addLeft(b, x);
        if(a-1 >= 0) this.addLeft(a-1, -x);
    }
    // 获取位置i的值
    getSum(i) {
        i += this.leafLen;
        let sum = 0;
        for( ; i; i>>=1){
            sum += this.arr[i];
        }
        return sum;
    }
}

例子:

var mt = new MySumTree(1024);
mt.addLeft(100, 10); // 将区间 [0,100] 内的每个数字都加 10
mt.addLeft(200, 20); // 将区间 [0,200] 内的每个数字都加 20
mt.getSum(100); // 获取位置 100 上的结果 //-> 30

20240607 rust实现

use std::collections::HashMap;

/// 个用数组存的完全二叉树,可以对区间[a,b]里的每个数加x,便于获取位置i累加后的和,
struct MySumTree {
    /// 叶子节点的长度,得是 2^n
    leaf_len: i32,
    /// 可以用数组,也可以用 map 存储; map[位置] = 路过该节点加几
    /// 数组的长度是子叶的二倍,arr[0]没用,arr[1]是根
    map: HashMap<i32, i32>,
}

impl MySumTree {
    pub fn new(len: i32) -> MySumTree {
        return MySumTree {
            leaf_len: 1 << ((len as f64).log2().ceil() as i32),
            map: HashMap::new(),
        }
    }
    /// 对区间[0,i]里的每个数加x
    pub fn add_0_i(&mut self, mut i:i32, x:i32) {
        if i<0 { return; }
        if i >= self.leaf_len-1 { // 全都加
            *self.map.entry(1).or_insert(0) += x; // map[根] += x;
        } else {
            i += self.leaf_len; // 找到叶子节点
            while i > 0 {
                if i&1 == 0{
                    *self.map.entry(i).or_insert(0) += x; // map[i] += x;
                    i -= 1; // 跳到邻近的左边的方块
                }
                i>>=1;
            }
        }
    }
    /// 对区间[i,len]里的每个数加x, 等价于全部加x,然后[0,i-1]加-x
    pub fn add_i_right(&mut self, i:i32, x:i32) {
        if i>self.leaf_len { return; }
        *self.map.entry(1).or_insert(0) += x; // map[根] += x;
        if i-1 >= 0 { self.add_0_i(i-1, -x); }
    }
    /// 对区间[a,b]里的每个数加x,等价于[0,b]加x,然后[0,a-1]加-x
    pub fn add_a_b(&mut self, a:i32, b:i32, x:i32){
        if b>=self.leaf_len { return; }
        if b>=0 { self.add_0_i(b, x); }
        if a-1 >= 0 { self.add_0_i(a-1, -x); }
    }
    /// 获取位置i的值
    pub fn get_sum(&mut self, mut i:i32) ->i32 {
        i += self.leaf_len;
        let mut sum = 0;
        while i > 0 {
            sum = sum + *self.map.entry(i).or_insert(0);
            i >>= 1;
        }
        return sum;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值