子数组范围和(2022-3-4)每日一练

2104. 子数组范围和(2022-3-4)

给你一个整数数组 numsnums 中,子数组的 范围 是子数组中最大元素和最小元素的差值。

返回 nums所有 子数组范围的

子数组是数组中一个连续 非空 的元素序列。

示例 1:

输入:nums = [1,2,3]
输出:4
解释:nums 的 6 个子数组如下所示:
[1],范围 = 最大 - 最小 = 1 - 1 = 0
[2],范围 = 2 - 2 = 0
[3],范围 = 3 - 3 = 0
[1,2],范围 = 2 - 1 = 1
[2,3],范围 = 3 - 2 = 1
[1,2,3],范围 = 3 - 1 = 2
所有范围的和是 0 + 0 + 0 + 1 + 1 + 2 = 4

示例 2:

输入:nums = [1,3,3]
输出:4
解释:nums 的 6 个子数组如下所示:
[1],范围 = 最大 - 最小 = 1 - 1 = 0
[3],范围 = 3 - 3 = 0
[3],范围 = 3 - 3 = 0
[1,3],范围 = 3 - 1 = 2
[3,3],范围 = 3 - 3 = 0
[1,3,3],范围 = 3 - 1 = 2
所有范围的和是 0 + 0 + 0 + 2 + 0 + 2 = 4

示例 3:

输入:nums = [4,-2,-3,4,1]
输出:59
解释:nums 中所有子数组范围的和是 59

提示:

  • 1 <= nums.length <= 1000
  • -109 <= nums[i] <= 109

**进阶:**你可以设计一种时间复杂度为 O(n) 的解决方案吗?

解题思路

方法一: 暴力枚举

没什么技术含量,但是好在写起来、理解起来都很简单。只需要在一系列子数组里维护一个最大最小值就好了。

var subArrayRanges = function(nums) {
    let len = nums.length
    let ans = 0
    for(let i = 0; i < len-1; i++){
        let max=-Infinity,min=Infinity
        for(let j = i; j < len; j++){
            max = nums[j] > max ? nums[j] : max
            min = nums[j] < min ? nums[j] : min
            ans += max - min
        }
    }
    return ans
};

方法二:单调栈、记录每个元素的作用次数

首先何为单调栈,「先进后出」可以通过数组尾部的方法pushpop来实现;单调递增、单调递减就是从小到大、从大到小。

究其子数组最大值减去最小值这个行为,其实就是被认为“大”的元素处于「被减数」,被认为“小”的元素处于「减数」;我们只需要知道当前元素nums[i]当了多少次「被减数」和「减数」。那每个元素的总和就是答案了。

维护一个单调栈的目的在于,去前边寻找比当前元素小的值,先统计当前元素作为「被减数」的次数

统计「减数」时也是同样维护一个单调栈。

var subArrayRanges = function(nums) {
    const n = nums.length
    let stack = [], ans = 0n
    for(let i = 0; i <= n; i++) {
        while(stack.length > 0 && (i == n || nums[stack[stack.length - 1]] < nums[i])) {
            const j = BigInt(stack.pop())
            console.log(i,j)
            ans += BigInt(nums[j]) * (BigInt(i) - j) * 
            (j - (stack.length > 0 ? BigInt(stack[stack.length - 1]) : -1n))
        }
        stack.push(i)
    }
    stack = []
    for(let i = 0; i <= n; i++) {
        while(stack.length > 0 && (i == n || nums[stack[stack.length - 1]] > nums[i])) {
            const j = BigInt(stack.pop())
            ans -= BigInt(nums[j]) * (BigInt(i) - j) * 
            (j - (stack.length > 0 ? BigInt(stack[stack.length - 1]) : -1n))
        }
        stack.push(i)
    }
    return ans
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值