【刷题日记】2104. 子数组范围和

简介: 对于很久没有刷题的我来说,突然开始也开始刷题了,不为别的,已经习惯参与掘金的活动了,同时也可以促进自己再回顾一下算法题,毕竟长时间不练,真的就生疏了

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

2104. 子数组范围和

对于很久没有刷题的我来说,突然开始也开始刷题了,不为别的,已经习惯参与掘金的活动了,同时也可以促进自己再回顾一下算法题,毕竟长时间不练,真的就生疏了


本月开始刷题了,尝试写题解,希望能够对你有帮助

一、题目描述:

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

返回 nums 中 所有 子数组范围的 和 。

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

image.png

二、思路分析:

1、这道题考察了什么思想?你的思路是什么?

看了这题的描述和示例,我们应该得到了如下几个信息

  • 从给出的数组中,要找到所有的子数组,子数组中的元素顺序是要和原有数组元素顺序是保持一致的
  • 对于每一个子数组,我们需要找到该子数组中的最大值,和最小值并计算差值
  • 需要将所有子数组的计算的结果,求和

对于这个子数组,需要注意的地方是,要是连续的,非空的

image.png

如果我们认为可以向上述这样组成子数组那么思考方向就是错误的,这里一定需要注意

根据以上已知信息,我们可以一步一步的来推导,先看如何找到子数组

由于是需要在子数组中找到最大数和最小数的差值,那么子数组中只有一个元素的情况,我们就直接忽略,首先根据题意,子数组要非空,那么若子数组只有 1 个元素,则最大数和最小数的差值是 0

我们可以用上述示例 3 来进行演示:

复制代码

4 , -2 ,-3,4, 1

image.png

根据上图,我们不难理解,

  • 就是先指定左边第 1 个元素为起始位置,然后不停的向右扩,扩充到的每一个子数组,计算最大值和最小值的差值,直到扩充到数组长度后
  • 再从左边起始的第 2 个元素,再继续向右扩
  • 最终,当起始元素指到数组的最右边的一个元素的时候,则查找完毕
  • 最终将所有的子数组的差值求和

2、尝试编码

那么,根据上述的图示和逻辑,咱们不难写出类似于这样的代码

image.png

这样的实现方式,对于大多数用例也是可以满足的,但是对于数量大一点的用例,实际情况是通过了 56 个用例但是对于数据复杂一点的用例就会出现时间超限的情况

image.png

仔细查看咱们写的代码,实际上是使用了 3 层 for 循环 ,这在生产环境中肯定是不允许的

因此,咱们一定有优化的空间

3、思考和探索

上述对于计算每一个子数组的最大值和最小值的做法是一样的,筛选出的子数组的方式也是一样的,那么,我们是否可以换一种思路来看看这个题,想办法筛选出子数组的 max ,和  min 分别对应放置,最终,直接计算对应的数据差值,再求和即可,这样就可以杜绝 3 个 for 循环了

image.png

例如上图,我们第一步,以及将 [4,-2] 最大值 和 最小值计算出来,那么 [4,-2,-3] 子数组的时候,只需要和   [4,-2] 的结果进行比较和计算即可

例如计算  [4,-2,-3] 最大值的时候,只需要 将  [4,-2] 子数组中的最大值 和  -3 进行比较,那么一定是   [4,-2,-3]  的最大值

最小值的计算逻辑也是一样,其他的子数组计算逻辑也是保持一致

那么,我们可以推算出如下这样的表格

按照上述的方式,将子数组的 max 放到 big 表格中,min 放到 small 表格中

image.png

[4,-2],计算出 max 为 4 放到 big 表格中,min 为 -2 放到 small 表格中

image.png

[4,-2,-3],计算出 max 为 4 放到 big 表格中,min 为 -3 放到 small 表格中

image.png

同理可以得出上述第一行的最大值和最小值, 其余行依次类推,最终得出如下表格:

image.png

源码如下:

func subArrayRanges(nums []int) int64 {
  length := len(nums)
  big := make([][]int, length)
  small := make([][]int, length)
  // small 表格 和 big 表格 ,对角线是 nums 数组元素
  for i := 0; i < length; i++ {
    big[i] = make([]int, length)
    big[i][i] = nums[i]
    small[i] = make([]int, length)
    small[i][i] = nums[i]
  }
  // 分别绘制 small 表格,和 big 表格
  for i := 1; i < length; i++ {
    for j := 0; j+i < length; j++ {
      big[j][j+i] = max(big[j][j+i-1], nums[j+i])
      small[j][j+i] = min(small[j][j+i-1], nums[j+i])
    }
  }
  var sum int
  for i := 0; i < length; i++ {
    for j := i + 1; j < length; j++ {
      sum += big[i][j] - small[i][j]
    }
  }
  return int64(sum)
}
func max(a, b int) int {
  if a > b {
    return a
  }
  return b
}
func min(a, b int) int {
  if a < b {
    return a
  }
  return b
}

当然,这样显的代码行数有点多,但是没有关系,咱们仔细思考一下,就不难理解官方的题解了

image.png

原理和我们上述推理过程一致,按照顺序找到子数组,后一个数组的计算参数,依赖于上一个子数组的结果

四、总结:

本题考察对应数组的应用,遍历子数组并进行相应的计算,这种方式时间复杂度是 O(n^2) , 空间复杂度是  O(1)

今天就到这里,学习所得,若有偏差,还请斧正


欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

image.png

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~

相关文章
|
6月前
|
算法 容器
OJ刷题日记:2、双指针(2)
OJ刷题日记:2、双指针(2)
43 0
|
6月前
OJ刷题日记:3、滑动窗口(1)
OJ刷题日记:3、滑动窗口(1)
55 0
|
6月前
|
容器
《剑指offer》——刷题日记
《剑指offer》——刷题日记
|
6月前
|
算法 索引
OJ刷题日记:5、二分查找(1)
OJ刷题日记:5、二分查找(1)
52 0
|
6月前
|
算法 测试技术
OJ刷题日记:1、双指针(1)
OJ刷题日记:1、双指针(1)
54 0
|
6月前
|
算法 C++ 索引
OJ刷题日记:4、滑动窗口(2)
OJ刷题日记:4、滑动窗口(2)
60 0
|
Cloud Native
【刷题日记】88. 合并两个有序数组
本次刷题日记的第 34 篇,力扣题为:88. 合并两个有序数组 ,简单
|
6月前
|
算法
六六力扣刷题数组之再刷二分法
六六力扣刷题数组之再刷二分法
43 0
|
索引 Cloud Native
【刷题日记】31. 下一个排列
【刷题日记】31. 下一个排列
|
索引 Cloud Native
【刷题日记】152. 乘积最大子数组
【刷题日记】152. 乘积最大子数组