leetcode
leetcode 2501 ~ 2550
找到最大非递减数组的长度

找到最大非递减数组的长度

难度:

标签:

题目描述

You are given a 0-indexed integer array nums.

You can perform any number of operations, where each operation involves selecting a subarray of the array and replacing it with the sum of its elements. For example, if the given array is [1,3,5,6] and you select subarray [3,5] the array will convert to [1,8,6].

Return the maximum length of a non-decreasing array that can be made after applying operations.

A subarray is a contiguous non-empty sequence of elements within an array.

 

Example 1:

Input: nums = [5,2,2]
Output: 1
Explanation: This array with length 3 is not non-decreasing.
We have two ways to make the array length two.
First, choosing subarray [2,2] converts the array to [5,4].
Second, choosing subarray [5,2] converts the array to [7,2].
In these two ways the array is not non-decreasing.
And if we choose subarray [5,2,2] and replace it with [9] it becomes non-decreasing. 
So the answer is 1.

Example 2:

Input: nums = [1,2,3,4]
Output: 4
Explanation: The array is non-decreasing. So the answer is 4.

Example 3:

Input: nums = [4,3,2,6]
Output: 3
Explanation: Replacing [3,2] with [5] converts the given array to [4,5,6] that is non-decreasing.
Because the given array is not non-decreasing, the maximum possible answer is 3.

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

代码结果

运行时间: 376 ms, 内存: 38.8 MB


/*
 * 思路:
 * 1. 使用Java Stream API来计算最长的非递减子数组长度。
 * 2. 将相邻元素做比较,判断是否非递减。
 * 3. 将每次的结果用流的方式传递,最终得到最大值。
 */
import java.util.stream.IntStream;

public class Solution {
    public int longestNonDecreasing(int[] nums) {
        return IntStream.range(1, nums.length)
                        .map(i -> nums[i] >= nums[i - 1] ? 1 : 0)
                        .reduce(1, (acc, curr) -> curr == 1 ? acc + 1 : Math.max(acc, 1));
    }
}

解释

方法:

这个题解的核心思路是使用动态规划和两个队列(deque)来跟踪可能的非递减子数组的状态。每次迭代中,通过计算前缀和来识别是否可以增加一个新的非递减子数组,或者更新现有的非递减子数组。具体来说,使用两个队列preQueue和curQueue来维护前一段和当前段的非递减子数组状态。每个队列的元素包含三个值:结合值、总值和前缀和。当当前前缀和大于等于最小结合值时,意味着可以形成一个新的子数组段。否则,更新当前队列的状态以尝试将当前值整合到现有的非递减子数组中。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在使用队列(preQueue和curQueue)来维护非递减子数组状态的过程中,每个队列元素中的'结合值'、'总值'和'前缀和'分别代表什么含义?
在此题解中,队列的每个元素代表一个潜在的非递减子数组的状态。'结合值'是指当前子数组与前一个子数组的连接点的值,它应该保证非递减性;'总值'是指从当前子数组开始到当前元素的累积总和;'前缀和'则是从数组开始到当前元素的累积和。这些值帮助算法判断何时应继续扩展当前子数组,何时应开始新的子数组,以及如何更新子数组的状态。
🦆
题解中提到,当当前前缀和大于等于最小结合值时,可以形成一个新的子数组段。这个'最小结合值'是如何计算出来的,它的逻辑依据是什么?
'最小结合值'是通过比较所有在preQueue中的'结合值'来计算的,选择最小的一个。逻辑依据是,为了维持子数组的非递减性,新的子数组的开始值(即前缀和的当前值)需要大于或等于前一个非递减子数组的结合值。这确保了新的子数组段的开始不会使整个数组失去非递减的特性。
🦆
在处理每个元素时,为什么需要从preQueue中删除前缀和小于等于当前和的元素?这样的操作对算法的正确性和效率有什么影响?
从preQueue中删除前缀和小于等于当前和的元素是为了确保所有保留在队列中的状态都是有可能用于未来数组段的。删除这些元素可以避免无用的计算和空间浪费,提高算法的效率。算法的正确性保持不变,因为被删除的状态已经被当前的前缀和超越,不会再对形成新的非递减子数组有贡献。
🦆
题解提出了每个元素最多被处理两次(一次添加,一次移除),这种处理方法如何确保不会漏掉某些可能的非递减子数组配置?
每个元素在被添加到队列时考虑的是当前元素能否形成或扩展非递减子数组,而在被移除时考虑的是它是否还能对形成新的非递减子数组有用。这种处理确保了每个元素的每个可能状态都被充分利用,同时维持了效率。因为每个状态的有效性都在它失效前被充分利用,所以不会漏掉任何可能的非递减子数组配置。

相关问题