计算所有连续子数组和的排序区间和 — 题解与实现
题目描述
给定一个数组 nums
,包含 n
个正整数。需要计算该数组中所有非空的连续子数组的和,并将这些和组成的新数组进行升序排序。新数组的长度为:
因为每个非空连续子数组都会对应一个和。
之后,要求计算排序后数组中从第 left
个数字到第 right
个数字(下标从 1 开始)的和,并将结果对 10^9 + 7 取模。
题目分析
- 所有非空连续子数组和的个数:对于长度为
n
的数组,有多少个非空连续子数组呢?
答案是。例如,长度为4的数组有10个非空连续子数组。
如何快速计算所有子数组的和?
计算所有子数组的和暴力方法是遍历所有 (i, j)
,求和;这会是 O(n^3) 的时间复杂度,很慢。
可以借助前缀和数组,令 prefix[i]
表示 nums[0]
到 nums[i-1]
的和。这样任意子数组和可以在 O(1) 时间内得到:
- 排序与区间求和
生成的所有子数组和存入一个数组,排序后即可按题目要求取区间[left, right]
和。 - 注意结果可能很大,需要对 10^9 + 7 取模。
解题方法
1. 计算前缀和数组
构造一个长度为 n+1
的前缀和数组 prefix
,其中:
prefix[0] = 0
prefix[i+1] = prefix[i] + nums[i]
2. 计算所有子数组和
遍历所有可能的子数组起点 i
和终点 j
,利用前缀和快速计算:
sums.append(prefix[j+1] - prefix[i])
这一步时间复杂度为 O(n^2)。
3. 排序
对所有子数组和排序,时间复杂度 。
4. 取区间和并对结果取模
计算排序数组中 [left-1:right]
的和,然后对 10^9+7 取模返回。
代码示例(Python)
from typing import List
class Solution:
def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int:
MOD = 10**9 + 7
# 1. 构造前缀和数组
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
# 2. 计算所有子数组和
sums = []
for i in range(n):
for j in range(i, n):
sums.append(prefix[j + 1] - prefix[i])
# 3. 排序
sums.sort()
# 4. 取区间和并取模
return sum(sums[left - 1:right]) % MOD
复杂度分析
- 时间复杂度
-
- 计算前缀和:O(n)
- 生成所有子数组和:O(n^2)
- 排序:
- 总体:
- 空间复杂度
-
- 前缀和数组:O(n)
- 子数组和数组:O(n^2)
示例说明
以数组 nums = [1, 2, 3, 4]
为例:
- 所有非空子数组和(共10个):
-
- [1] = 1
- [1, 2] = 3
- [1, 2, 3] = 6
- [1, 2, 3, 4] = 10
- [2] = 2
- [2, 3] = 5
- [2, 3, 4] = 9
- [3] = 3
- [3, 4] = 7
- [4] = 4
排序后:
- [1, 2, 3, 3, 4, 5, 6, 7, 9, 10]
- 若题目要求计算第
3
到第6
个数的和,则区间是[3, 3, 4, 5]
,和为 3 + 3 + 4 + 5 = 15。
总结与扩展
- 该题最直观的解法是通过前缀和 + 全部枚举 + 排序实现。
- 对于较大规模的数组,这种
的算法可能性能不佳。
- 如果要进一步优化,可以尝试利用二分法+优先队列的思路,按顺序找到第 k 小的子数组和,或者使用线段树等高级数据结构。
- 但对于中小规模问题,这种暴力解法简单易懂,且实现方便。