LC-1567. 乘积为正数的最长子数组长度

128 阅读2分钟

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

题目描述

给你一个整数数组 nums ,请你求出乘积为正数的最长子数组的长度。

一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。

请你返回乘积为正数的最长子数组长度。

示例 1:

输入: nums = [1,-2,-3,4]
输出: 4
解释: 数组本身乘积就是正数,值为 24 。

示例 2:

输入:nums = [0,1,-2,-3,-4]
输出:3
解释:最长乘积为正数的子数组为 [1,-2,-3] ,乘积为 6 。
注意,我们不能把 0 也包括到子数组中,因为这样乘积为 0 ,不是正数。

示例 3:

输入: nums = [-1,-2,-3,0,1]
输出: 2
解释: 乘积为正数的最长子数组是 [-1,-2] 或者 [-2,-3]

提示:

  • 1 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9

题解

这道题 还是用的贪心的思路。

根据上一道题 LC-152. 乘积最大子数组 的思路,依旧是从前往后从后往前各来一遍,只不过代码量确实有点多了。

这里给出一段图片,方便解题

image.png

这道题,要的是长度,所以我们不需要计算结果。只需要考虑正负顺序即可

贪心1

这里定义了3个不同的count来分别存储,和一个flag来判断。

  • cout1 代表第一段正数

  • cout2 代表第二段正数

  • cout3 代表遇到的第一个负数

const getMaxLen = (nums) => {
  const numsLength = nums.length

  let max = 0
  let count1 = 0
  let count2 = 0
  let count3 = 0
  let flag = false

  // 重置
  const reset = () => {
    count1 = 0
    count2 = 0
    count3 = 0
    flag = false
  }

  for (let i = 0; i < numsLength; i++) {
    const val = nums[i]

    if (val > 0) {
      if (!flag) count1++
      if (flag) count2++
    }

    if (val < 0) {
      flag = !flag

      if (flag) count3++

      if (!flag) {
        count1 = count1 + count2 + count3 + 1
        count2 = 0
        count3 = 0
      }
    }

    if (val === 0 || i === numsLength - 1) {
      if (flag) {
        let currentMax = Math.max(count1, count2)
        max = Math.max(max, currentMax)
      }

      if (!flag) {
        max = Math.max(max, count1)
      }

      reset()
    }
  }

  for (let i = numsLength - 1; i >= 0; i--) {
    const val = nums[i]

    if (val > 0) {
      if (!flag) count1++
      if (flag) count2++
    }

    if (val < 0) {
      flag = !flag

      if (flag) count3++

      if (!flag) {
        count1 = count1 + count2 + count3 + 1
        count2 = 0
        count3 = 0
      }
    }

    if (val === 0 || i === 0) {
      if (flag) {
        let currentMax = Math.max(count1, count2)
        max = Math.max(max, currentMax)
      }

      if (!flag) {
        max = Math.max(max, count1)
      }

      reset()
    }
  }

  return max
}

贪心2

这个一样的道理,但是代码量会减少很多。

这里指的都是当前某一段,遇到0,则会重置。

  • positiveCount 遇到的正数的个数

  • negativeCount 遇到的负数的个数

  • firstNegativePositon 遇到的第一个负数的位置

const getMaxLen = (nums) => {
  const numsLength = nums.length
  let positiveCount = 0
  let negativeCount = 0
  let firstNegativePositon = -1
  let max = 0

  for (let i = 0; i < numsLength; i++) {
    const val = nums[i]

    if (val > 0) {
      positiveCount++
    }

    if (val < 0) {
      // 记录该段第一个负数出现的位置
      if (firstNegativePositon == -1) {
        firstNegativePositon = i
      }
      negativeCount++
    }

    if (val === 0) {
      // 遇到0所有标记初始化
      positiveCount = 0
      negativeCount = 0
      firstNegativePositon = -1
    }

    if (negativeCount % 2 == 0) {
      // 当前段落,全为正数,则直接将正负的值进行相加。之后进行比较
      max = Math.max(max, positiveCount + negativeCount)
    } else {
      // firstNegativePositon 代表第一个出现的负数
      // (firstNegativePositon,i] 为止所有的数乘积都为正数
      max = Math.max(max, i - firstNegativePositon)
    }
  }
  return max
}

总结

题目 35 :贪心算法是没有固定的框架,思路是重要的,就是要局部找到最优解。