题目
给你一个数组 nums ,它包含 n 个正整数。你需要计算所有非空连续子数组的和,并将它们按升序排序,得到一个新的包含 n * (n + 1) / 2 个数字的数组。
请你返回在新数组中下标为 left 到 right (下标从 1 开始)的所有数字和(包括左右端点)。由于答案可能很大,请你将它对 10^9 + 7 取模后返回。
示例1
输入:nums = [1,2,3,4], n = 4, left = 1, right = 5
输出:13
解释:所有的子数组和为 1, 3, 6, 10, 2, 5, 9, 3, 7, 4 。将它们升序排序后,我们得到新的数组 [1, 2, 3, 3, 4, 5, 6, 7, 9, 10] 。下标从 le = 1 到 ri = 5 的和为 1 + 2 + 3 + 3 + 4 = 13 。
题解
暴力循环
题目数据量,暴力循环时间复杂度为,可以AC的;代码如下
var rangeSum = function (nums, n, left, right) {
let array = []
for (let i = 0; i < n; i++) {
let t = 0
for (let j = i; j < n; j++) {
t += nums[j]
array.push(t)
}
}
array.sort((a, b) => a - b)
const total = array.slice(left - 1, right)
let result = 0
for (let i = left - 1; i < right; i++) {
result += array[i]
result = result % 1000000007
}
return result
}
小顶堆+贪心
暴力循环中将所有数据计算出来,然后排序,时间复杂度为,但是在本题中只需要知道排序后[left,right]之间的数据,没必要将所有数据计算出来。现在一个思考方向,计算数组并且计算得到的结果是在数组中递增的。
依nums = [1,2,3,4]为例;
- 构建数组list = [[1,0],[2,1],[3,2],[4,3]];中二维数组第一位为值,第二位表示当前值所在原数组的下标;将list放入小顶堆;
- 小顶堆弹出小顶堆堆顶,放入数组result,result得到[1,0];
- 将弹出的值,处理,再次压入小顶堆;如何处理?判断弹出的数据下标是否到达数组尾部,如果没有到达,将数组当前值+数组下标+1值压入堆中,比如第一次得到[1,0]处理 后得到[1+2,1];
- 这时堆中数据为[[2,1],[1+2,1],[3,2],[4,3]]
- 继续弹出小顶堆堆顶[2,1],放入result,result得到[[1,0],[2,1]];
- 处理弹出值[2,1] ==> [2+3,1+1];压入堆中;
- 得到堆[[1+2,1],[3,2],[4,3],[2+3,2]]
- 继续弹出小顶堆堆顶[1+2,1],放入result,result得到[[1,0],[2,1],[1+2,1]];
- 处理弹出值[1+2,1] ==> [1+2+3,1+1];压入堆中;
- 得到栈[[3,2],[4,3],[2+3,2],[1+2+3,2]]
- ...
- 知道result.length === right;
- 枚举result所有数据,累加[left,right]和且对1000000007求余即可
代码
class Heap {
constructor(fn) {
this.list = [0]
this.compose = typeof fn === 'function' ? fn : this.defaultFn
}
defaultFn(a, b) {
return a < b
}
top() {
return this.list[1]
}
left(x) {
return 2 * x
}
right(x) {
return 2 * x + 1
}
parent(x) {
return x >> 1
}
isEmpty() {
return this.list.length === 1
}
getSize() {
return this.list.length - 1
}
push(value) {
this.list.push(value)
this.up(this.list.length - 1)
}
up(k) {
const { compose, list, parent } = this
const size = this.getSize()
while (size > 1 && compose(list[k], list[parent(k)])) {
this.wrap(k, parent(k))
k = parent(k)
}
}
pop() {
const size = this.getSize()
if (size === 0) return null
this.wrap(1, this.list.length - 1)
const top = this.list.pop()
this.down(1)
return top
}
down(k) {
const size = this.getSize()
const { left, right, compose, list } = this
while (left(k) <= size) {
let next = left(k)
if (right(k) <= size && compose(list[right(k)], list[next])) {
next = right(k)
}
if (compose(list[k], list[next])) return
this.wrap(k, next)
k = next
}
}
wrap(x, y) {
const t = this.list[x]
this.list[x] = this.list[y]
this.list[y] = t
}
}
var rangeSum = function (nums, n, left, right) {
const len = nums.length
const list = nums.map((v, i) => [v, i])
const minHead = new Heap((a, b) => a[0] < b[0])
const result = []
for (let i = 0; i < n; i++) {
minHead.push(list[i])
}
while (!minHead.isEmpty()) {
const [t, index] = minHead.pop()
if (index < len - 1) {
minHead.push([t + nums[index + 1], index + 1])
}
result.push(t)
if (result.length === right) {
const t = result.slice(left - 1, right).reduce((a, b) => a + b)
return t % 1000000007
}
}
}