Article / 文章

LeetCode 第327题:区间和的个数

给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的区间和的个数。 区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。

📖 文章摘要

本文详细解析LeetCode第327题“区间和的个数”,这是一道困难级别的前缀和和归并排序问题。文章提供了前缀和+归并排序和树状数组两种解法,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合想要提升高级算法应用能力的程序员。

核心知识点: 前缀和、归并排序、树状数组
难度等级: 困难
推荐人群: 具有扎实算法基础,想要提升高级算法应用能力的程序员

题目描述

给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的区间和的个数。

区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。

示例

示例 1:

输入:nums = [-2,5,-1], lower = -2, upper = 2
输出:3
解释:存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。

示例 2:

输入:nums = [0], lower = 0, upper = 0
输出:1

提示

  • 1 <= nums.length <= 10^5
  • -2^31 <= nums[i] <= 2^31 - 1
  • -10^5 <= lower <= upper <= 10^5
  • 题目数据保证答案是一个 32 位的整数

解题思路

方法一:前缀和 + 归并排序

使用前缀和数组转化问题,然后用归并排序的思想解决。

关键点:

  • 使用前缀和转化区间和问题
  • 使用归并排序过程中统计符合条件的区间数
  • 处理整数溢出问题
  • 正确维护区间计数

具体步骤:

  1. 计算前缀和数组
  2. 使用归并排序处理前缀和数组
  3. 在归并过程中统计符合条件的区间数
  4. 合并两个有序数组

时间复杂度:O(nlogn) 空间复杂度:O(n)

方法二:树状数组

使用树状数组维护前缀和的计数。

关键点:

  • 离散化前缀和数组
  • 使用树状数组维护计数
  • 动态更新和查询区间计数
  • 处理数值范围问题

具体步骤:

  1. 计算前缀和数组
  2. 离散化所有可能的前缀和值
  3. 使用树状数组维护计数
  4. 统计符合条件的区间数

时间复杂度:O(nlogn) 空间复杂度:O(n)

图解思路

前缀和计算过程表

索引 原数组 前缀和 区间和范围 符合条件的区间数
0 -2 -2 [-2,-2] 1
1 5 3 [-2,3] 1
2 -1 2 [-2,2] 1

归并排序过程表

步骤 左半部分 右半部分 合并结果 统计的区间数
1 [-2] [3] [-2,3] 1
2 [-2,3] [2] [-2,2,3] 2

代码实现

C# 实现

public class Solution {
    private int count = 0;
    private long[] temp;
    
    public int CountRangeSum(int[] nums, int lower, int upper) {
        long[] sums = new long[nums.Length + 1];
        temp = new long[nums.Length + 1];
        
        // 计算前缀和
        for (int i = 0; i < nums.Length; i++) {
            sums[i + 1] = sums[i] + nums[i];
        }
        
        // 归并排序统计区间数
        MergeSort(sums, 0, sums.Length - 1, lower, upper);
        return count;
    }
    
    private void MergeSort(long[] sums, int left, int right, int lower, int upper) {
        if (left >= right) return;
        
        int mid = left + (right - left) / 2;
        MergeSort(sums, left, mid, lower, upper);
        MergeSort(sums, mid + 1, right, lower, upper);
        
        // 统计符合条件的区间数
        int i = left;
        int l = mid + 1, r = mid + 1;
        while (i <= mid) {
            while (l <= right && sums[l] - sums[i] < lower) l++;
            while (r <= right && sums[r] - sums[i] <= upper) r++;
            count += r - l;
            i++;
        }
        
        // 合并两个有序数组
        Merge(sums, left, mid, right);
    }
    
    private void Merge(long[] sums, int left, int mid, int right) {
        int i = left, j = mid + 1, k = left;
        
        while (i <= mid && j <= right) {
            if (sums[i] <= sums[j]) {
                temp[k++] = sums[i++];
            } else {
                temp[k++] = sums[j++];
            }
        }
        
        while (i <= mid) temp[k++] = sums[i++];
        while (j <= right) temp[k++] = sums[j++];
        
        for (i = left; i <= right; i++) {
            sums[i] = temp[i];
        }
    }
}

Python 实现

class Solution:
    def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
        def mergeSort(sums, left, right, lower, upper):
            if left >= right:
                return 0
                
            mid = (left + right) // 2
            count = mergeSort(sums, left, mid, lower, upper) + \
                    mergeSort(sums, mid + 1, right, lower, upper)
            
            # 统计符合条件的区间数
            i = left
            l = r = mid + 1
            while i <= mid:
                while l <= right and sums[l] - sums[i] < lower:
                    l += 1
                while r <= right and sums[r] - sums[i] <= upper:
                    r += 1
                count += r - l
                i += 1
            
            # 合并两个有序数组
            temp = sorted(sums[left:right + 1])
            for i in range(len(temp)):
                sums[left + i] = temp[i]
            
            return count
        
        # 计算前缀和
        sums = [0] * (len(nums) + 1)
        for i in range(len(nums)):
            sums[i + 1] = sums[i] + nums[i]
        
        return mergeSort(sums, 0, len(sums) - 1, lower, upper)

C++ 实现

class Solution {
private:
    int count = 0;
    vector<long long> temp;
    
    void mergeSort(vector<long long>& sums, int left, int right, int lower, int upper) {
        if (left >= right) return;
        
        int mid = left + (right - left) / 2;
        mergeSort(sums, left, mid, lower, upper);
        mergeSort(sums, mid + 1, right, lower, upper);
        
        // 统计符合条件的区间数
        int i = left;
        int l = mid + 1, r = mid + 1;
        while (i <= mid) {
            while (l <= right && sums[l] - sums[i] < lower) l++;
            while (r <= right && sums[r] - sums[i] <= upper) r++;
            count += r - l;
            i++;
        }
        
        // 合并两个有序数组
        merge(sums, left, mid, right);
    }
    
    void merge(vector<long long>& sums, int left, int mid, int right) {
        int i = left, j = mid + 1, k = left;
        
        while (i <= mid && j <= right) {
            if (sums[i] <= sums[j]) {
                temp[k++] = sums[i++];
            } else {
                temp[k++] = sums[j++];
            }
        }
        
        while (i <= mid) temp[k++] = sums[i++];
        while (j <= right) temp[k++] = sums[j++];
        
        for (i = left; i <= right; i++) {
            sums[i] = temp[i];
        }
    }
    
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        vector<long long> sums(nums.size() + 1);
        temp.resize(nums.size() + 1);
        
        // 计算前缀和
        for (int i = 0; i < nums.size(); i++) {
            sums[i + 1] = sums[i] + nums[i];
        }
        
        mergeSort(sums, 0, sums.size() - 1, lower, upper);
        return count;
    }
};

执行结果

C# 实现

  • 执行用时:108 ms
  • 内存消耗:42.8 MB

Python 实现

  • 执行用时:1524 ms
  • 内存消耗:30.2 MB

C++ 实现

  • 执行用时:52 ms
  • 内存消耗:17.4 MB

性能对比

语言 执行用时 内存消耗 特点
C# 108 ms 42.8 MB 实现简洁,性能适中
Python 1524 ms 30.2 MB 代码最简洁,但性能较差
C++ 52 ms 17.4 MB 性能最优

代码亮点

  1. 🎯 巧妙使用前缀和转化问题
  2. 💡 在归并排序过程中统计区间数
  3. 🔍 处理整数溢出问题
  4. 🎨 代码结构清晰,模块化设计

常见错误分析

  1. 🚫 没有处理整数溢出问题
  2. 🚫 区间统计逻辑错误
  3. 🚫 归并排序实现不当
  4. 🚫 边界条件处理不当

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
前缀和+归并排序 O(nlogn) O(n) 思路清晰 实现复杂
树状数组 O(nlogn) O(n) 更新查询快 需要离散化

相关题目


📖 系列导航

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

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


💬 互动交流

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

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

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

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

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