前缀和算法解析:牛客网NC20242区间统计问题

发布时间:2026/10/26 13:04:16
前缀和算法解析:牛客网NC20242区间统计问题

1. 项目背景与核心价值

"小红统计区间"是牛客网算法题库中的一道经典题目,编号为NC20242。这道题在编程面试准备阶段具有极高的训练价值,尤其适合准备校招笔试和算法竞赛的初学者。作为一道被标记为"easy"难度的题目,它能够帮助学习者快速掌握前缀和算法的核心思想。

我在刷题过程中发现,很多初次接触区间统计问题的同学容易陷入暴力求解的误区。这道题的精妙之处在于,它用看似简单的题干(给定一个数组和区间范围,统计满足条件的子区间数量),引导我们思考如何用O(n)时间复杂度替代O(n²)的暴力解法。

2. 问题描述与抽象建模

2.1 原始题目描述

给定一个长度为n的数组a,以及两个整数L和R。要求统计数组中连续子数组(即子区间)的个数,这些子数组的元素和大于等于L且小于等于R。

输入示例:

a = [1,2,3] L = 2 R = 3

输出示例:

3

解释:满足条件的子区间为[1,2](和为3)、[2](和为2)、[3](和为3)

2.2 问题抽象化

这个问题可以抽象为:在给定数组的所有连续子区间中,找出满足sum ∈ [L, R]的区间数量。关键在于如何高效计算这些区间,避免O(n²)的暴力枚举。

3. 算法思路解析

3.1 暴力解法及其缺陷

最直观的解法是双重循环枚举所有子区间:

count = 0 n = len(a) for i in range(n): current_sum = 0 for j in range(i, n): current_sum += a[j] if L <= current_sum <= R: count += 1 return count

时间复杂度O(n²),当n较大时(如1e5)会超时。

3.2 前缀和优化思路

前缀和(Prefix Sum)是解决区间求和问题的利器。我们预处理一个前缀和数组prefix,其中prefix[i]表示a[0]到a[i-1]的和。这样任意区间[i,j]的和可以表示为prefix[j+1] - prefix[i]。

但直接应用前缀和仍需双重循环,我们需要更聪明的办法。

3.3 数学转换技巧

将问题拆解为:

  1. 计算区间和≤R的数量(记为count_R)
  2. 计算区间和<L的数量(记为count_L)
  3. 最终结果 = count_R - count_L

这样就将原问题转化为两个更易处理的子问题。

4. 最优解法实现

4.1 基于前缀和的有序统计

使用归并排序的思想统计前缀和的有序性:

def countRangeSum(nums, lower, upper): prefix = [0] for num in nums: prefix.append(prefix[-1] + num) def merge_sort(l, r): if l == r: return 0 mid = (l + r) // 2 count = merge_sort(l, mid) + merge_sort(mid+1, r) # 统计满足条件的区间数 i = j = mid + 1 for left in prefix[l:mid+1]: while i <= r and prefix[i] - left < lower: i += 1 while j <= r and prefix[j] - left <= upper: j += 1 count += j - i # 合并两个有序数组 prefix[l:r+1] = sorted(prefix[l:r+1]) return count return merge_sort(0, len(prefix)-1)

4.2 时间复杂度分析

采用分治策略后,时间复杂度降至O(n log n),空间复杂度O(n)。这是目前最优的解决方案。

5. 代码实现细节

5.1 边界条件处理

特别注意几个关键边界:

  1. 空数组情况
  2. L > R的情况
  3. 整数溢出问题(当元素值很大时)

5.2 测试用例设计

完整的测试应包含:

test_cases = [ ([], 0, 0, 0), # 空数组 ([1,2,3], 2, 3, 3), # 示例 ([1,1,1], 1, 2, 5), # 重复元素 ([-1,-2,-3], -3, -2, 3), # 负数情况 ([10**9]*1000, 10**9, 10**9, 1000) # 大数据测试 ]

6. 同类问题扩展

掌握这个解法后,可以解决一系列变种问题:

  1. 乘积小于K的子数组个数(LeetCode 713)
  2. 和等于K的子数组个数(LeetCode 560)
  3. 最接近K的子数组和(LeetCode 363)

7. 刷题经验分享

在牛客网刷题时,我有几个实用建议:

  1. 先自己思考20分钟再查看题解
  2. 对每道题记录3种解法:暴力→优化→最优
  3. 建立错题本,记录错误原因和突破点
  4. 定期复习已AC的题目,确保真正掌握

这道题教会我们:看似简单的区间统计问题,往往蕴含着精妙的算法思想。前缀和+分治的组合拳,是解决这类问题的利器。