区域和检索 - 数组不可变

标签: 设计 数组 前缀和

难度: Easy

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

  1. 计算索引 left 和 right (包含 leftright)之间的 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
  • 最多调用 104sumRange 方法

Submission

运行时间: 60 ms

内存: 20.0 MB

class NumArray:

    def __init__(self, nums: List[int]):
        n = len(nums)
        # 前缀和数组
        self.presum = [0]*(n+1)
        # 计算 nums 的累加和
        for i in range(1,n+1):
            self.presum[i] = self.presum[i-1] + nums[i-1]

    def sumRange(self, left: int, right: int) -> int:
        # 查询闭区间 [left, right] 的累加和
        sumlr = self.presum[right+1] - self.presum[left]
        return sumlr




# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(left,right)

Explain

这个题解使用了前缀和的思想。在构造函数中,先预处理计算出数组 nums 的前缀和数组 presum,其中 presum[i] 表示 nums[0] 到 nums[i-1] 的累加和。在 sumRange 查询函数中,利用前缀和数组快速计算出区间 [left, right] 的累加和,只需要用 presum[right+1] 减去 presum[left] 即可得到结果。

时间复杂度: O(n)

空间复杂度: O(n)

class NumArray:

    def __init__(self, nums: List[int]):
        n = len(nums)
        # 创建前缀和数组,长度为 n+1,方便处理边界情况
        self.presum = [0]*(n+1) 
        # 计算 nums 的前缀和
        for i in range(1, n+1):
            self.presum[i] = self.presum[i-1] + nums[i-1]

    def sumRange(self, left: int, right: int) -> int:
        # 查询闭区间 [left, right] 的累加和
        # 利用前缀和数组,只需计算 presum[right+1] - presum[left]
        sumlr = self.presum[right+1] - self.presum[left]
        return sumlr

Explore

在前缀和数组`presum`中,索引0的值被初始化为0,这主要是为了方便计算从数组开头到某一位置的累加和。具体来说,当需要计算从`nums[0]`到`nums[right]`的累加和时,直接使用`presum[right+1]`即可得到结果。这种设计避免了在计算区间和时需要进行特殊判断或额外的计算来处理从数组开头开始的累加情况。

在构造`NumArray`类时,创建的前缀和数组`presum`长度为`n+1`,其中`n`是输入数组`nums`的长度。因此在循环计算前缀和时,`presum[i]`的索引从1开始,最大到`n`,对应的`nums[i-1]`的索引从0开始,最大到`n-1`。这样设计保证了访问`nums[i-1]`时,索引始终在有效范围内,不会越界。

方法`sumRange`使用`presum[right+1] - presum[left]`来计算区间[left, right]的和的原理基于前缀和的定义。`presum[i]`表示从`nums[0]`到`nums[i-1]`的累加和。因此,`presum[right+1]`表示从`nums[0]`到`nums[right]`的累加和,而`presum[left]`表示从`nums[0]`到`nums[left-1]`的累加和。从`presum[right+1]`中减去`presum[left]`,就剔除了`nums[0]`到`nums[left-1]`的部分,恰好得到从`nums[left]`到`nums[right]`的累加和。

题解中的`sumRange`方法并没有直接提到对索引`left`或`right`超出数组有效范围的处理机制。在实际应用中,应当在方法调用前或方法内部添加索引合法性的检查,例如确保`0 <= left <= right < nums.length`。如果不进行这样的检查,试图访问超出范围的前缀和数组`presum`的元素可能导致运行时错误。