news 2026/4/15 16:33:23

算法题 子数组的最小值之和

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 子数组的最小值之和

907. 子数组的最小值之和

问题描述

给定一个整数数组arr,计算所有非空连续子数组的最小值之和。由于答案可能很大,返回结果对10^9 + 7取模。

示例

输入: arr = [3,1,2,4] 输出: 17 解释: 子数组为 [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4]。 最小值分别为 3, 1, 2, 4, 1, 1, 2, 1, 1, 1,和为 17。

算法思路

单调栈

  1. 对于每个元素arr[i],计算它作为最小值能"贡献"到多少个子数组中
  2. 找到左边第一个小于arr[i]的位置left和右边第一个小于等于arr[i]的位置right
  3. arr[i]为最小值的子数组数量为(i - left) * (right - i)
  4. 总贡献为arr[i] * (i - left) * (right - i)

为什么右边用"小于等于"而左边用"小于"?

  • 避免重复计算:当有相同元素时,统一规定只有最左边的那个元素负责计算包含这些相同元素的子数组
  • 确保每个子数组的最小值只被计算一次

代码实现

方法一:单调栈

classSolution{privatestaticfinalintMOD=1000000007;/** * 计算所有子数组最小值之和 * 使用单调栈计算每个元素的贡献度 * * @param arr 输入数组 * @return 所有子数组最小值之和(对10^9+7取模) */publicintsumSubarrayMins(int[]arr){intn=arr.length;// left[i] 表示 arr[i] 左边第一个小于 arr[i] 的元素位置,不存在则为 -1int[]left=newint[n];// right[i] 表示 arr[i] 右边第一个小于等于 arr[i] 的元素位置,不存在则为 nint[]right=newint[n];// 计算 left 数组 - 使用单调递增栈Deque<Integer>stack=newArrayDeque<>();for(inti=0;i<n;i++){// 维护单调递增栈,弹出大于等于当前元素的索引while(!stack.isEmpty()&&arr[stack.peek()]>=arr[i]){stack.pop();}// 如果栈为空,说明左边没有更小的元素left[i]=stack.isEmpty()?-1:stack.peek();stack.push(i);}// 清空栈,准备计算 right 数组stack.clear();// 计算 right 数组 - 使用单调递增栈for(inti=n-1;i>=0;i--){// 维护单调递增栈,弹出大于当前元素的索引while(!stack.isEmpty()&&arr[stack.peek()]>arr[i]){stack.pop();}// 如果栈为空,说明右边没有更小或相等的元素right[i]=stack.isEmpty()?n:stack.peek();stack.push(i);}longresult=0;// 计算每个元素的贡献度for(inti=0;i<n;i++){// 以 arr[i] 为最小值的子数组数量longcount=(long)(i-left[i])*(right[i]-i);// 累加贡献度result=(result+arr[i]*count)%MOD;}return(int)result;}}

方法二:一次遍历

classSolution{privatestaticfinalintMOD=1000000007;/** * 在单调栈弹出元素时直接计算贡献度 * * @param arr 输入数组 * @return 所有子数组最小值之和(对10^9+7取模) */publicintsumSubarrayMins(int[]arr){intn=arr.length;Deque<Integer>stack=newArrayDeque<>();longresult=0;// 添加哨兵元素简化边界处理for(inti=0;i<=n;i++){// 当 i == n 时,当前值设为 -1(确保弹出所有剩余元素)intcurrent=(i==n)?-1:arr[i];// 当栈不为空且当前元素小于栈顶元素时while(!stack.isEmpty()&&current<arr[stack.peek()]){// 弹出栈顶元素intmid=stack.pop();// 左边界:栈顶元素(如果栈为空则为 -1)intleft=stack.isEmpty()?-1:stack.peek();// 右边界:当前索引 iintright=i;// 计算以 arr[mid] 为最小值的子数组数量longcount=(long)(mid-left)*(right-mid);result=(result+(long)arr[mid]*count)%MOD;}stack.push(i);}return(int)result;}}

算法分析

  • 时间复杂度:O(n)
    • 每个元素最多入栈和出栈一次,总体线性时间
  • 空间复杂度:O(n)
    • 需要栈空间和辅助数组(方法一)或仅栈空间(方法二)

算法过程

输入:arr = [3,1,2,4]

方法一

  1. 计算 left 数组

    • i=0: 栈空 →left[0] = -1, 栈: [0]
    • i=1:arr[0]=3 >= arr[1]=1→ 弹出0,栈空 →left[1] = -1, 栈: [1]
    • i=2:arr[1]=1 < arr[2]=2left[2] = 1, 栈: [1,2]
    • i=3:arr[2]=2 < arr[3]=4left[3] = 2, 栈: [1,2,3]
    • left = [-1, -1, 1, 2]
  2. 计算 right 数组

    • i=3: 栈空 →right[3] = 4, 栈: [3]
    • i=2:arr[3]=4 > arr[2]=2→ 弹出3,栈空 →right[2] = 4, 栈: [2]
    • i=1:arr[2]=2 > arr[1]=1→ 弹出2,栈空 →right[1] = 4, 栈: [1]
    • i=0:arr[1]=1 < arr[0]=3right[0] = 1, 栈: [1,0]
    • right = [1, 4, 4, 4]
  3. 计算贡献度

    • i=0:count = (0-(-1)) * (1-0) = 1*1 = 1, 贡献 =3*1 = 3
    • i=1:count = (1-(-1)) * (4-1) = 2*3 = 6, 贡献 =1*6 = 6
    • i=2:count = (2-1) * (4-2) = 1*2 = 2, 贡献 =2*2 = 4
    • i=3:count = (3-2) * (4-3) = 1*1 = 1, 贡献 =4*1 = 4
    • 总和 =3+6+4+4 = 17

测试用例

publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例int[]arr1={3,1,2,4};System.out.println("Test 1: "+solution.sumSubarrayMins(arr1));// 17// 测试用例2:递增数组int[]arr2={1,2,3,4};System.out.println("Test 2: "+solution.sumSubarrayMins(arr2));// 20// [1]+[2]+[3]+[4]+[1,2]+[2,3]+[3,4]+[1,2,3]+[2,3,4]+[1,2,3,4]// = 1+2+3+4+1+2+3+1+2+1 = 20// 测试用例3:递减数组int[]arr3={4,3,2,1};System.out.println("Test 3: "+solution.sumSubarrayMins(arr3));// 20// 每个子数组的最小值都是最后一个元素// 测试用例4:相同元素int[]arr4={2,2,2};System.out.println("Test 4: "+solution.sumSubarrayMins(arr4));// 12// [2]+[2]+[2]+[2,2]+[2,2]+[2,2,2] = 2+2+2+2+2+2 = 12// 测试用例5:单元素int[]arr5={5};System.out.println("Test 5: "+solution.sumSubarrayMins(arr5));// 5// 测试用例6:大数值(测试取模)int[]arr6={100000};System.out.println("Test 6: "+solution.sumSubarrayMins(arr6));// 100000// 测试用例7:复杂情况int[]arr7={71,55,82,55};System.out.println("Test 7: "+solution.sumSubarrayMins(arr7));// 539// 测试用例8:包含0int[]arr8={3,0,2,1};System.out.println("Test 8: "+solution.sumSubarrayMins(arr8));// 7}

关键点

  1. 贡献度

    • 不直接枚举所有子数组,而是计算每个元素作为最小值的贡献
  2. 单调栈

    • 用于高效找到每个元素左右边界
    • 左边找"小于",右边找"小于等于"避免重复计算
  3. 边界处理

    • 不存在更小元素时,左边界为-1,右边界为n
    • 计算的子数组数量公式统一为(i-left) * (right-i)
  4. 数据类型

    • 使用long防止中间计算溢出
    • 最后对10^9 + 7取模
  5. 哨兵

    • 方法二中在数组末尾添加哨兵元素,简化边界处理
    • 确保所有元素最终都会被弹出并计算贡献

常见问题

  1. 为什么右边用"小于等于"而左边用"小于"?

    • 处理重复元素的情况,确保每个子数组只被计算一次
    • 如果两边都用"小于",重复元素会被重复计算
    • 如果两边都用"小于等于",可能会漏掉某些情况
  2. (i-left) * (right-i)公式?

    • (i-left)表示以arr[i]为右端点的左边界选择数量
    • (right-i)表示以arr[i]为左端点的右边界选择数量
    • 两者相乘就是所有包含arr[i]arr[i]为最小值的子数组数量
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 8:52:31

CUDA不可用时的选择:M2FP CPU版保障基础AI服务能力

CUDA不可用时的选择&#xff1a;M2FP CPU版保障基础AI服务能力 在当前AI应用快速落地的背景下&#xff0c;GPU已成为深度学习推理服务的标配硬件。然而&#xff0c;在实际部署中&#xff0c;仍存在大量无CUDA支持的边缘设备或低配服务器环境——如本地开发机、老旧工作站、嵌入…

作者头像 李华
网站建设 2026/4/14 16:54:38

基于SpringBoot的三七原产地销售平台设计与实现

一、平台开发背景与意义 三七作为云南等地的特色中药材&#xff0c;具有较高的药用价值和市场需求&#xff0c;但当前销售环节存在诸多痛点&#xff1a;产地农户缺乏直接触达消费者的渠道&#xff0c;依赖中间商导致利润压缩&#xff1b;消费者难以辨别三七的产地真伪、品质等级…

作者头像 李华
网站建设 2026/4/14 23:12:16

基于SpringBoot的农产品溯源系统设计与实现

一、系统开发背景与意义 随着食品安全意识的提升&#xff0c;消费者对农产品的产地、种植过程、质检信息的关注度日益增高。但当前农产品流通环节存在信息不透明、溯源链条断裂等问题&#xff0c;部分商家虚假宣传、以次充好&#xff0c;导致消费者信任度降低。传统溯源方式依赖…

作者头像 李华
网站建设 2026/4/13 10:52:43

MGeo在城市积水点预警系统中的地址匹配

MGeo在城市积水点预警系统中的地址匹配 引言&#xff1a;城市内涝治理中的精准定位挑战 随着城市化进程加速&#xff0c;极端天气频发&#xff0c;城市内涝问题日益突出。在智慧城市建设背景下&#xff0c;积水点预警系统成为提升城市应急管理能力的关键环节。然而&#xff0c;…

作者头像 李华
网站建设 2026/4/11 10:25:31

Z-Image-Turbo输出目录管理:自定义保存路径与命名规则

Z-Image-Turbo输出目录管理&#xff1a;自定义保存路径与命名规则 引言&#xff1a;从默认输出到工程化文件管理 在使用阿里通义Z-Image-Turbo WebUI进行AI图像生成的过程中&#xff0c;用户往往关注提示词优化、参数调优和生成质量&#xff0c;却容易忽视一个关键环节——输出…

作者头像 李华
网站建设 2026/4/11 23:56:28

MGeo对‘村+组’层级农村地址的识别能力

MGeo对“村组”层级农村地址的识别能力 引言&#xff1a;农村地址识别的现实挑战与MGeo的技术突破 在城乡融合发展的背景下&#xff0c;农村地区的数字化治理需求日益增长。然而&#xff0c;农村地址结构复杂、命名不规范、层级模糊等问题长期制约着地理信息系统的精准应用。尤…

作者头像 李华