307. Range Sum Query - Mutable

本文介绍了一种使用段树(segment tree)进行数组更新与区间求和的有效方法。通过构建段树,能够高效地处理数组元素更新及指定区间的元素求和。文章详细解释了段树的构建过程,并提供了具体的 Rust 代码实现。

Given an integer array nums, handle multiple queries of the following types:

Update the value of an element in nums.
Calculate the sum of the elements of nums between indices left and right inclusive where left <= right.
Implement the NumArray class:

NumArray(int[] nums) Initializes the object with the integer array nums.
void update(int index, int val) Updates the value of nums[index] to be val.
int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive (i.e. nums[left] + nums[left + 1] + … + nums[right]).

Example 1:

Input

[“NumArray”, “sumRange”, “update”, “sumRange”]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
Output
[null, 9, null, 8]

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

Constraints:

  • 1 <= nums.length <= 3 * 104
  • -100 <= nums[i] <= 100
  • 0 <= index < nums.length
  • -100 <= val <= 100
  • 0 <= left <= right < nums.length
  • At most 3 * 104 calls will be made to update and sumRange.

从这一题学到了一个新的数据结构, segment tree, 但是拿数组实现的时候没有成功, 或者说是没有好的方法来构建, 于是就用最简单的二叉树形式来实现了。

segment tree 简单来说就是把一个数组不停的二分, 每个分出来的 slice 作为一个节点, 叶子节点为数组中的单个元素。每个节点可以保存加和、平均值之类的与其对应的 slice 有关的数据。 就这个题来讲, 我们在每个节点上保存了 slice 的加和, 查询的时候,如果查询的范围正好是当前节点 slice 的范围, 则我们直接返回这个加和, 否则的话继续向下查询。更新叶子节点的时候我们选择更新差值(新值-旧值), 从根节点开始遍历,所走过的节点的 slice 只要包含该叶子节点那该节点的加和就需要更新成 sum + diff。


use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct NumArray {
    nums: Vec<i32>,
    tree: Option<Rc<RefCell<Node>>>,
}

#[derive(Debug)]
struct Node {
    start: usize,
    end: usize,
    sum: i32,
    left: Option<Rc<RefCell<Node>>>,
    right: Option<Rc<RefCell<Node>>>,
}

impl Node {
    fn new(start: usize, end: usize, sum: i32) -> Self {
        Self {
            start: start,
            end: end,
            sum: sum,
            left: None,
            right: None,
        }
    }
}

fn update(root: &Option<Rc<RefCell<Node>>>, index: usize, diff: i32) {
    if let Some(node) = root {
        let mut b = node.borrow_mut();
        if b.start <= index && b.end >= index {
            b.sum += diff;
            update(&b.left, index, diff);
            update(&b.right, index, diff);
        }
    }
}

fn query(root: &Option<Rc<RefCell<Node>>>, start: usize, end: usize) -> i32 {
    if let Some(node) = root {
        let b = node.borrow();
        if start > b.end || end < b.start {
            return 0;
        }
        if b.start >= start && b.end <= end {
            return b.sum;
        }
        let left = query(&b.left, start, end);
        let right = query(&b.right, start, end);
        return left + right;
    }
    0
}

fn build_tree(nums: &Vec<i32>, start: usize, end: usize) -> Option<Rc<RefCell<Node>>> {
    if start == end {
        return Some(Rc::new(RefCell::new(Node::new(start, end, nums[start]))));
    }
    let sum: i32 = nums[start..=end].iter().map(|v| *v).sum();
    let mut node = Node::new(start, end, sum);
    let left = build_tree(nums, start, start + (end - start) / 2);
    let right = build_tree(nums, start + (end - start) / 2 + 1, end);
    node.left = left;
    node.right = right;
    Some(Rc::new(RefCell::new(node)))
}

/**
 * `&self` means the method takes an immutable reference.
 * If you need a mutable reference, change it to `&mut self` instead.
 */
impl NumArray {
    fn new(nums: Vec<i32>) -> Self {
        let tree = build_tree(&nums, 0, nums.len() - 1);
        Self { nums, tree }
    }

    fn update(&mut self, index: i32, val: i32) {
        let diff = val - self.nums[index as usize];
        update(&self.tree, index as usize, diff);
        self.nums[index as usize] = val;
    }

    fn sum_range(&self, left: i32, right: i32) -> i32 {
        query(&self.tree, left as usize, right as usize)
    }
}
这段命令是 **IntelliJ IDEA** 在运行 JUnit 测试时生成的 Java 启动命令,主要用于调试和执行单元测试。以下是关键部分的解析: --- ### **核心组成部分** 1. **Java 执行路径** ```bash C:\Users\admin\.jdks\ms-11.0.28\bin\java.exe ``` - 使用 JDK 11(AdoptOpenJDK/MS 发行版)运行程序。 2. **调试代理(Debug Agent)** ```bash -javaagent:C:\Users\admin\AppData\Local\JetBrains\IntelliJIdea2025.1\captureAgent\debugger-agent.jar=... ``` - IntelliJ 的调试器代理,用于捕获运行时状态(如断点、变量值)。 3. **IDEA 运行时配置** ```bash -javaagent:E:\IdeaPro\IntelliJ IDEA 2025.1.4.1\lib\idea_rt.jar=63575 ``` - IntelliJ 自带的运行时工具,支持控制台输出重定向到 IDE。 4. **JVM 参数** ```bash -ea # 启用断言(Assertions) -Dfile.encoding=UTF-8 # 设置文件编码为 UTF-8 -Dkotlinx.coroutines.debug... # Kotlin 协程调试相关配置 ``` 5. **测试目标** ```bash com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.tplink.nbu.cloudstorage.appserver.service.impl.WeeklyReportServiceImplTest,testCalculate ``` - 运行 `WeeklyReportServiceImplTest` 类中的 `testCalculate` 测试方法(JUnit 4)。 --- ### **关键参数说明** | 参数 | 作用 | |------|------| | `-javaagent` | 加载 Java 代理(用于调试、性能监控等) | | `-ea` | 启用 `assert` 关键字的功能 | | `-Dfile.encoding=UTF-8` | 避免中文乱码 | | `@C:\Users\admin\AppData\Local\Temp\idea_arg_fileXXX` | 从临时文件加载其他参数 | --- ### **典型场景** 1. **调试单元测试** - 当你在 IntelliJ 中点击 `Run Test` 或 `Debug Test` 时,IDE 会自动生成此命令。 - 调试代理(`debugger-agent.jar`)会监听断点和变量。 2. **协程调试** - Kotlin 协程相关的参数(如 `-Dkotlinx.coroutines.debug.enable.creation.stack.trace`)用于跟踪协程调用栈。 --- ### **常见问题** 1. **为什么需要这么多参数?** - IntelliJ 通过代理和 JVM 参数实现高级调试功能(如热部署、协程跟踪)。 2. **如何手动简化命令?** - 直接运行测试可以简化为: ```bash java -cp <classpath> org.junit.runner.JUnitCore com.tplink.nbu.cloudstorage.appserver.service.impl.WeeklyReportServiceImplTest ``` - 但会失去调试和 IDE 集成功能。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值