Bootstrap

检测相邻递增子数组 II - LeetCode 3350 解题思路与代码解析

检测相邻递增子数组 II - LeetCode 3350 解题思路与代码解析

在本篇博客中,我们将深入解析一道中等难度的算法题——检测相邻递增子数组 II。通过这道题,我们将学习如何高效地处理数组中的递增子数组问题,并理解解决该问题的最佳策略。

题目描述

给定一个由 n 个整数组成的数组 nums,请你找出 k 的最大值,使得存在两个相邻且长度为 k 的严格递增子数组。具体来说,需要检查是否存在从下标 ab (a < b) 开始的两个子数组,并满足下述全部条件:

  1. 这两个子数组 nums[a..a + k - 1]nums[b..b + k - 1] 都是严格递增的。
  2. 这两个子数组必须是相邻的,即 b = a + k

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

示例

示例 1:

输入:nums = [2,5,7,8,9,2,3,4,3,1]
输出:3
解释:
从下标 2 开始的子数组是 [7, 8, 9],它是严格递增的。
从下标 5 开始的子数组是 [2, 3, 4],它也是严格递增的。
这两个子数组是相邻的,因此 3 是满足题目条件的最大 k 值。

示例 2:

输入:nums = [1,2,3,4,4,4,4,5,6,7]
输出:2
解释:
从下标 0 开始的子数组是 [1, 2],它是严格递增的。
从下标 2 开始的子数组是 [3, 4],它也是严格递增的。
这两个子数组是相邻的,因此 2 是满足题目条件的最大 k 值。

提示

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

解题思路

要找到满足条件的最大 k,即存在两个相邻且长度为 k 的严格递增子数组,我们需要有效地遍历数组,并跟踪每个严格递增子数组的长度。

关键点在于:

  1. 识别严格递增的子数组:我们需要遍历数组,识别出所有严格递增的子数组,并记录它们的长度。
  2. 检查相邻性:确保找到的两个子数组是相邻的,即第二个子数组紧跟在第一个子数组的后面。
  3. 寻找最大 k:在所有可能的相邻子数组对中,找到最大的 k 值。

为了实现上述目标,我们可以采用以下策略:

  • 遍历数组:逐个元素检查当前元素是否比前一个元素大,以确定是否延续了当前的递增子数组。
  • 记录递增子数组的长度:当遇到不再递增的元素时,记录当前递增子数组的长度,并重置计数器以开始新的子数组。
  • 比较相邻子数组的长度:每次记录一个递增子数组的长度时,与之前一个子数组的长度进行比较,更新 k 的值。

代码解析

以下是解决该问题的 Python 代码实现:

class Solution:
    def maxIncreasingSubarrays(self, nums: List[int]) -> int:
        n = len(nums)
        ans = pre_cnt = cnt = 0
        for i, x in enumerate(nums):
            cnt += 1
            # 如果当前元素是最后一个,或者下一个元素不再递增
            if i == n - 1 or x >= nums[i + 1]:
                # 计算可能的 k 值
                # cnt // 2 是因为需要两个子数组,每个至少长度 k
                # min(pre_cnt, cnt) 确保两个子数组长度一致
                ans = max(ans, cnt // 2, min(pre_cnt, cnt))
                pre_cnt = cnt
                cnt = 0
        return ans

代码详解

  1. 初始化变量

    • n:数组的长度。
    • ans:存储当前找到的最大 k 值。
    • pre_cnt:记录上一个严格递增子数组的长度。
    • cnt:当前正在计数的严格递增子数组的长度。
  2. 遍历数组

    • 使用 enumerate 函数遍历数组的每个元素及其索引。
    • 对于每个元素 x,将 cnt 增加 1,表示当前递增子数组长度增加。
  3. 检测递增子数组的结束

    • 如果当前元素是最后一个元素 (i == n - 1),或者当前元素不小于下一个元素 (x >= nums[i + 1]),则说明当前递增子数组结束。
  4. 更新最大 k

    • 计算 cnt // 2,因为需要两个子数组,每个至少长度 k
    • pre_cntcnt 的最小值,确保两个子数组的长度一致。
    • 更新 ans 为当前找到的最大值。
  5. 重置计数器

    • pre_cnt 更新为当前的 cnt,为下一个递增子数组做准备。
    • cnt 重置为 0,开始新的计数。
  6. 返回结果

    • 最终返回 ans,即满足条件的最大 k 值。

示例解析

示例 1

输入:nums = [2,5,7,8,9,2,3,4,3,1]
  • 递增子数组识别:

    • [2,5,7,8,9] 长度为 5
    • [2,3,4] 长度为 3
    • [3] 长度为 1
    • [1] 长度为 1
  • 相邻子数组对:

    • [2,5,7,8,9][2,3,4]:k = min(5,3) = 3
  • 最大 k 为 3。

示例 2

输入:nums = [1,2,3,4,4,4,4,5,6,7]
  • 递增子数组识别:

    • [1,2,3,4] 长度为 4
    • [4] 长度为 1
    • [4] 长度为 1
    • [4,5,6,7] 长度为 4
  • 相邻子数组对:

    • [1,2,3,4][4]:k = min(4,1) = 1
    • [4][4]:k = min(1,1) = 1
    • [4][4,5,6,7]:k = min(1,4) = 1
  • 最大 k 为 1。

注意:在第二个示例中,虽然 [1,2,3,4][4,5,6,7] 都是严格递增的,但它们不是直接相邻的,因为中间有一个单独的 [4]

复杂度分析

  • 时间复杂度O(n),其中 n 是数组的长度。我们只需要遍历数组一次。
  • 空间复杂度O(1),只使用了常数级别的额外空间。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;