当前位置:首页 > 技术文章 > 正文内容

2025-08-02:最多 K 个元素的子数组的最值之和。用go语言,给定一

2025-08-02:最多 K 个元素的子数组的最值之和。用go语言,给定一个整数数组 nums 和一个正整数 k,请找出所有长度最多为 k 的连续子数组,计算这些子数组中最大值和最小值的和,并返回最大的那个和。

1 <= nums.length <= 80000。

1 <= k <= nums.length。

-1000000 <= nums[i] <= 1000000。

输入:nums = [1,2,3], k = 2。

输出:20。

解释:

最多 2 个元素的 nums 的子数组:

当然,下面是你提供的数据转换成的表格形式:

子数组

最小值

最大值

[1]

1

1

2

[2]

2

2

4

[3]

3

3

6

[1, 2]

1

2

3

[2, 3]

2

3

5

总和



20

输出为 20 。

题目来自力扣3430。

解决思路

直接暴力计算所有子数组的最大值和最小值的和显然是不现实的,因为子数组的数量是 O(n * k),对于 n = 80000k = 80000 来说,这会非常慢。因此,我们需要一种更高效的方法。

关键观察

1. 单调栈:我们可以利用单调栈来计算所有子数组的最小值的贡献和最大值的贡献。具体来说:

o 对于最小值,我们需要计算每个元素作为最小值出现在多少个子数组中,然后乘以该元素的值,得到其贡献。

o 对于最大值,类似地,我们需要计算每个元素作为最大值出现在多少个子数组中,然后乘以该元素的值,得到其贡献。

o 最终结果是所有子数组的最大值的和加上所有子数组的最小值的和(即 sum(max) + sum(min))。

2. 限制子数组长度:我们需要限制子数组的长度不超过 k。因此,在计算某个元素作为最小值或最大值时,需要考虑子数组的长度限制。

具体步骤

1. 计算最小值的贡献

o 使用单调递增栈来找到每个元素作为最小值可以覆盖的范围(即左右边界)。

o 对于每个元素 nums[i],找到左边第一个比它小的元素的位置 l 和右边第一个比它小的元素的位置 r。这样,nums[i][l+1, r-1] 区间内的最小值。

o 计算以 nums[i] 为最小值的子数组的数量(考虑长度不超过 k),然后乘以 nums[i] 得到其贡献。

o 具体来说,子数组的数量可以通过组合数学计算:在 [l+1, r-1] 区间内,包含 nums[i] 的子数组的数量,且长度不超过 k

2. 计算最大值的贡献

o 类似地,我们可以通过将所有元素取反,然后复用计算最小值的逻辑来计算最大值的贡献。

o 因为 max(a, b) = -min(-a, -b),所以对 nums 取反后计算最小值等价于原数组的最大值。

3. 合并结果

o 计算所有子数组的最小值的和 sum_min

o 计算所有子数组的最大值的和 sum_max(通过取反后计算 sum_min 得到)。

o 最终结果是 sum_max + sum_min

子数组数量的计算

对于一个元素 nums[i],其作为最小值的范围是 [l+1, r-1],其中 l 是左边第一个比它小的位置,r 是右边第一个比它小的位置。子数组的数量可以通过以下方式计算:

o 子数组必须包含 nums[i],且长度不超过 k

o 设 m = r - l - 1nums[i] 作为最小值的区间长度。

  • 如果 m <= k,则子数组数量为 m * (m + 1) / 2(即所有可能的子数组)。
  • 如果 m > k,则需要考虑长度限制。子数组的数量为 (2*m - k + 1) * k / 2

时间复杂度

o 单调栈的处理是 O(n) 的,因为每个元素最多入栈和出栈一次。

o 对于每个元素,计算子数组数量的操作是 O(1) 的。

o 因此,总的时间复杂度是 O(n)

额外空间复杂度

o 单调栈的空间是 O(n)

o 其他临时变量的空间是 O(1)

o 因此,总的额外空间复杂度是 O(n)

总结

1. 使用单调栈计算每个元素作为最小值和最大值的贡献。

2. 通过数学公式计算限制子数组长度不超过 k 时的子数组数量。

3. 合并最小值和最大值的贡献得到最终结果。

4. 时间复杂度:O(n)

5. 额外空间复杂度:O(n)

Go完整代码如下:

.

package main

import (
    "fmt"
    "math"
)

func minMaxSubarraySum(nums []int, k int)int64 {
    count := func(m int)int {
        if m <= k {
            return (m + 1) * m / 2
        }
        return (m*2 - k + 1) * k / 2
    }

    // 计算最小值的贡献
    sumSubarrayMins := func() (res int) {
        st := []int{-1} // 哨兵
        for r, x := range nums {
            forlen(st) > 1 && nums[st[len(st)-1]] >= x {
                i := st[len(st)-1]
                st = st[:len(st)-1]
                l := st[len(st)-1]
                cnt := count(r-l-1) - count(i-l-1) - count(r-i-1)
                res += nums[i] * cnt // 累加贡献
            }
            st = append(st, r)
        }
        return
    }

    nums = append(nums, math.MinInt)
    ans := sumSubarrayMins()
    // 所有元素取反(求最大值),就可以复用同一份代码了
    for i := range nums {
        nums[i] = -nums[i]
    }
    ans -= sumSubarrayMins()
    returnint64(ans)
}

func main() {
    nums := []int{1, 2, 3}
    k := 2
    result := minMaxSubarraySum(nums, k)
    fmt.Println(result)
}




Python完整代码如下:

.

# -*-coding:utf-8-*-

import math

def minMaxSubarraySum(nums, k):
    def count(m):
        if m <= k:
            return (m + 1) * m // 2
        return (m * 2 - k + 1) * k // 2

    def sumSubarrayMins():
        res = 0
        st = [-1]  # 哨兵
        for r in range(len(nums)):
            x = nums[r]
            while len(st) > 1 and nums[st[-1]] >= x:
                i = st.pop()
                l = st[-1]
                cnt = count(r - l - 1) - count(i - l - 1) - count(r - i - 1)
                res += nums[i] * cnt  # 累加贡献
            st.append(r)
        return res

    # 计算最小值的贡献
    original_nums = nums.copy()
    nums.append(-math.inf)
    ans = sumSubarrayMins()
    
    # 所有元素取反(求最大值),就可以复用同一份代码了
    nums = original_nums.copy()
    nums = [-x for x in nums]
    nums.append(-math.inf)
    ans -= sumSubarrayMins()
    
    return ans

nums = [1, 2, 3]
k = 2
result = minMaxSubarraySum(nums, k)
print(result)





·



我们相信Go语言和算法为普通开发者提供了困境的“面试利器”,并致力于分享全面的编程知识。在这里,您可以找到最新的Go语言教程、算法解析、提升面试竞争力的秘籍以及行业动态。


欢迎关注“福大规模架构师每日一题”,让 Go 语言和算法助力您的职业发展

·

相关文章

「2022」打算跳槽涨薪,必问面试题及答案——VUE篇

1、为什么选择VUE,解决了什么问题?vue.js 正如官网所说的,是一套构建用户界面的渐进式框架。与其它重量级框架不同的是,vue 被设计为可以自底向上逐层应用。vue 的核心库只关注视图层,不仅易...

gitlab简单搭建与应用(gitlab怎么用)

一、gitlab1、简介GitLab是利用Ruby on Rails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。与Github类似,GitLab...

02.Web大前端时代之:HTML5+CSS3入门系列~H5结构元素

Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html1.结构元素 可以理解为语义话标记,比如:以前这么写<...

HTML5设计与制作哪家强?全省50多所高职院校齐聚中山比拼

3月22日下午,2018-2019年广东省职业院校学生专业技能大赛“HTML5交互融媒体内容设计与制作”赛项在中山火炬职业技术学院开幕。全省51所高职院校的52支参赛队伍参加此次大赛。参赛师生将于3月...

详解HTML5培训课程行业标准(html5课程总结)

需要HTML5培训必须先了解HTML5前景,没前景的职业,我们绝对不去入坑,这是正常人的思维。因此学习HTML5还的要多了解一些,目前HTML5技术已经日趋成熟,从国内热潮来看很多企业已开始使用,所以...

育知HTML5培训,为什么要学习“HTML5混合式开发技术”

HTML5 的广泛应用,强势崛起企业现在安卓、iOS开发人员都在学习HTML5混合开发,节约成本、一专多能是未来很多企业用人趋势!HTML5工程师在今后的工作中与 Android、iOS工程师对接的几...