LeetCode2024年3月14日每日一题(2789. 合并后数组中的最大元素)

文章介绍了单调栈在算法设计中的应用,特别是解决具有特定模式的问题,如寻找最长递增子序列。给出了一个实例,展示了如何使用单调栈在给定数组中找到最大累加子数组的值,通过维护单调性实现O(n)时间复杂度。
摘要由CSDN通过智能技术生成

单调栈

单调栈是一种特殊的数据结构,它在算法设计中被广泛使用,尤其是在处理与栈相关的问题时,如括号匹配、最长有效括号子串、最小窗口子串等。单调栈的核心思想是栈内的元素保持某种单调性(递增或递减),这使得它在处理特定问题时比普通栈更有效率。

单调栈的定义如下:

  1. 单调性:单调栈中的元素必须满足某种单调顺序。这种单调性可以是递增的,也可以是递减的。例如,一个递增单调栈中的元素从栈底到栈顶是严格递增的;一个递减单调栈则是严格递减的。

  2. 栈的操作:单调栈支持基本的栈操作,如入栈(push)和出栈(pop)。此外,它可能还支持其他操作,如查询当前栈顶元素、栈内最小/最大元素等。

  3. 维护单调性:在进行入栈操作时,必须确保新元素不违反栈的单调性。如果新元素违反了单调性,那么需要先从栈中移除一些元素,直到栈恢复单调性,然后再将新元素入栈。

  4. 应用场景:单调栈通常用于解决需要在序列中找到特定模式的问题,如找到最长的连续递增子序列、最小公共区间覆盖问题等。

举个例子,如果我们使用单调栈来找到最长的递增子序列,栈中的每个元素都比前一个元素大。当我们遇到一个不满足递增条件的元素时,我们会从栈中弹出元素,直到栈顶元素小于或等于当前元素,然后再次尝试将当前元素入栈。这样,我们可以确保栈中的子序列是最长的递增子序列。

单调栈的关键在于它能够利用栈的后进先出(LIFO)特性,以及栈内元素的有序性,来快速定位和解决问题。通过维护栈的单调性,我们可以在 O(n) 的时间复杂度内解决许多问题,其中 n 是序列的长度。
给你一个下标从 0 开始、由正整数组成的数组 nums 。

你可以在数组上执行下述操作 任意 次:

选中一个同时满足 0 <= i < nums.length - 1 和 nums[i] <= nums[i + 1] 的整数 i 。将元素 nums[i + 1] 替换为 nums[i] + nums[i + 1] ,并从数组中删除元素 nums[i] 。
返回你可以从最终数组中获得的 最大 元素的值。

示例 1:

输入:nums = [2,3,7,9,3]
输出:21
解释:我们可以在数组上执行下述操作:

  • 选中 i = 0 ,得到数组 nums = [5,7,9,3] 。
  • 选中 i = 1 ,得到数组 nums = [5,16,3] 。
  • 选中 i = 0 ,得到数组 nums = [21,3] 。
    最终数组中的最大元素是 21 。可以证明我们无法获得更大的元素。
    示例 2:

输入:nums = [5,3,3]
输出:11
解释:我们可以在数组上执行下述操作:

  • 选中 i = 1 ,得到数组 nums = [5,6] 。
  • 选中 i = 0 ,得到数组 nums = [11] 。
    最终数组中只有一个元素,即 11

代码的核心逻辑如下:

  1. 创建一个 LinkedList 类型的变量 list作为一个单调栈,用于存储当前遍历过程中的累加值。

  2. 从数组的末尾开始向前遍历 nums 数组,即从 nums.length-10。因为题目要求的是在nums[i] <= nums[i + 1] 这种情况下便可以合并,所以我们便选择从后往前遍历这个数组,将其加入到一个单调递增栈中,如果准备入栈的元素比栈顶的元素小,便将两者累加后再加入栈中,反之则直接入栈。刚好可以满足所有的累加满足nums[i] <= nums[i + 1]的条件,因为是一个单调递增栈,所以在结束遍历后,栈顶元素就是结果。

  3. 对于每个位置 i,将 nums[i] 的值赋给变量 cur,这代表当前位置的初始累加值。

  4. 使用一个 while 循环来处理 list,确保 list 中的最后一个元素(即当前累加的最大值)不小于 cur。如果不满足这个条件,说明当前的 cur 值比之前的累加值小,需要从 list 中移除最后一个元素,并将其值加到 cur 上。这个过程实际上是在寻找一个更大的累加值,以便重新开始累加。

  5. while 循环结束后,将更新后的 cur 值添加到 list 的末尾。

  6. 遍历完成后,list 中的最后一个元素将是遍历过程中遇到的所有可能累加值中的最大值。使用 list.getLast() 方法获取并返回这个最大值。

这种方法不是传统的动态规划解法,而是利用了单调栈的性质来解决问题。在这个特定的实现中,LinkedList 被用作栈,但它的 removeLast()addLast() 方法被用来维护一个单调递减的序列。这样做的好处是避免了传统动态规划中需要维护的复杂的状态转移方程,同时保持了 O(n) 的时间复杂度。

需要注意的是,这段代码中的注释部分(// System.out.println(list);)是用来调试的,它可以帮助开发者在每一步中查看 list 的当前状态,但在实际的生产代码中通常会被移除。

class Solution {
    public long maxArrayValue(int[] nums) {
        LinkedList<Long> list = new LinkedList<>();
        for (int i = nums.length-1; i >= 0; i--) {
            long cur=nums[i];
            while (!list.isEmpty()&&list.getLast()>=cur){
                long last= list.removeLast();
                cur+=last;
            }
            list.addLast(cur);
           // System.out.println(list);
        }
        return list.getLast();
    }
}
  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值