Article / 文章

LeetCode 第303题:区域和检索 - 数组不可变

给定一个整数数组 nums,处理以下类型的多个查询: 1. 计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和 ,其中 left <= right 实现 NumArray 类: - NumArray(int[] nums) 使用数组 nums 初始化对象 - int sumRange(int i, int j)

📖 文章摘要

本文详细解析LeetCode第303题“区域和检索 - 数组不可变”,这是一道考察前缀和的简单难度题目。文章提供了暴力求和和前缀和两种实现方案,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合学习前缀和和数组处理的读者。

核心知识点: 前缀和、数组处理、查询优化
难度等级: 简单
推荐人群: 具备基础算法知识,想要提升数组处理和查询优化能力的开发者

题目描述

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

  1. 计算索引 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 方法

解题思路

本题可以使用两种方法来实现:

  1. 暴力求和法:

    • 每次查询时遍历区间求和
    • 不需要额外空间
    • 查询时间复杂度O(n)
    • 适合查询次数少的情况
  2. 前缀和法:

    • 预处理计算前缀和数组
    • 利用前缀和快速计算区间和
    • 查询时间复杂度O(1)
    • 适合频繁查询的情况

图解思路

前缀和计算表

索引 原数组 前缀和 计算过程
0 -2 -2 -2
1 0 -2 -2 + 0
2 3 1 -2 + 0 + 3
3 -5 -4 1 + (-5)
4 2 -2 -4 + 2
5 -1 -3 -2 + (-1)

区间和查询步骤表

查询区间 计算方式 结果 时间复杂度
[0,2] preSum[2] - preSum[-1] 1 O(1)
[2,5] preSum[5] - preSum[1] -1 O(1)
[0,5] preSum[5] - preSum[-1] -3 O(1)

代码实现

C# 实现

public class NumArray {
    private int[] preSum;
    
    public NumArray(int[] nums) {
        preSum = new int[nums.Length + 1];
        for (int i = 0; i < nums.Length; i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
    }
    
    public int SumRange(int left, int right) {
        return preSum[right + 1] - preSum[left];
    }
}

Python 实现

class NumArray:
    def __init__(self, nums: List[int]):
        self.preSum = [0] * (len(nums) + 1)
        for i in range(len(nums)):
            self.preSum[i + 1] = self.preSum[i] + nums[i]
    
    def sumRange(self, left: int, right: int) -> int:
        return self.preSum[right + 1] - self.preSum[left]

C++ 实现

class NumArray {
private:
    vector<int> preSum;
    
public:
    NumArray(vector<int>& nums) {
        preSum.resize(nums.size() + 1);
        for (int i = 0; i < nums.size(); i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
    }
    
    int sumRange(int left, int right) {
        return preSum[right + 1] - preSum[left];
    }
};

执行结果

C# 实现

  • 执行用时:124 ms
  • 内存消耗:45.2 MB

Python 实现

  • 执行用时:68 ms
  • 内存消耗:17.8 MB

C++ 实现

  • 执行用时:16 ms
  • 内存消耗:17.1 MB

性能对比

语言 执行用时 内存消耗 特点
C# 124 ms 45.2 MB 实现简单,内存占用较大
Python 68 ms 17.8 MB 代码简洁,性能适中
C++ 16 ms 17.1 MB 性能最优,内存占用小

代码亮点

  1. 🎯 使用前缀和优化查询效率
  2. 💡 巧妙处理边界情况
  3. 🔍 空间换时间的经典应用
  4. 🎨 代码结构清晰,易于维护

常见错误分析

  1. 🚫 前缀和数组大小错误
  2. 🚫 边界索引计算错误
  3. 🚫 未考虑空数组情况
  4. 🚫 查询范围验证不完整

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
暴力求和 O(n) O(1) 空间占用小 查询慢
前缀和 O(1) O(n) 查询快 需要额外空间

相关题目

📖 系列导航

🔥 算法专题合集 - 查看完整合集

📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第303题。

💬 互动交流

感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。

如果这篇文章对你有帮助,请:

  • 👍 点个赞,让更多人看到这篇文章
  • 📁 收藏文章,方便后续查阅复习
  • 🔔 关注作者,获取更多高质量算法题解
  • 💭 评论区留言,分享你的解题思路或提出疑问

你的支持是我持续分享的动力!

💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!