这里写目录标题
单调栈
单调栈是一种特殊的数据结构,它在算法设计中被广泛使用,尤其是在处理与栈相关的问题时,如括号匹配、最长有效括号子串、最小窗口子串等。单调栈的核心思想是栈内的元素保持某种单调性(递增或递减),这使得它在处理特定问题时比普通栈更有效率。
单调栈的定义如下:
-
单调性:单调栈中的元素必须满足某种单调顺序。这种单调性可以是递增的,也可以是递减的。例如,一个递增单调栈中的元素从栈底到栈顶是严格递增的;一个递减单调栈则是严格递减的。
-
栈的操作:单调栈支持基本的栈操作,如入栈(push)和出栈(pop)。此外,它可能还支持其他操作,如查询当前栈顶元素、栈内最小/最大元素等。
-
维护单调性:在进行入栈操作时,必须确保新元素不违反栈的单调性。如果新元素违反了单调性,那么需要先从栈中移除一些元素,直到栈恢复单调性,然后再将新元素入栈。
-
应用场景:单调栈通常用于解决需要在序列中找到特定模式的问题,如找到最长的连续递增子序列、最小公共区间覆盖问题等。
举个例子,如果我们使用单调栈来找到最长的递增子序列,栈中的每个元素都比前一个元素大。当我们遇到一个不满足递增条件的元素时,我们会从栈中弹出元素,直到栈顶元素小于或等于当前元素,然后再次尝试将当前元素入栈。这样,我们可以确保栈中的子序列是最长的递增子序列。
单调栈的关键在于它能够利用栈的后进先出(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
代码的核心逻辑如下:
-
创建一个
LinkedList
类型的变量list
作为一个单调栈,用于存储当前遍历过程中的累加值。 -
从数组的末尾开始向前遍历
nums
数组,即从nums.length-1
到0
。因为题目要求的是在nums[i] <= nums[i + 1]
这种情况下便可以合并,所以我们便选择从后往前遍历这个数组,将其加入到一个单调递增栈中,如果准备入栈的元素比栈顶的元素小,便将两者累加后再加入栈中,反之则直接入栈。刚好可以满足所有的累加满足nums[i] <= nums[i + 1]
的条件,因为是一个单调递增栈,所以在结束遍历后,栈顶元素就是结果。 -
对于每个位置
i
,将nums[i]
的值赋给变量cur
,这代表当前位置的初始累加值。 -
使用一个
while
循环来处理list
,确保list
中的最后一个元素(即当前累加的最大值)不小于cur
。如果不满足这个条件,说明当前的cur
值比之前的累加值小,需要从list
中移除最后一个元素,并将其值加到cur
上。这个过程实际上是在寻找一个更大的累加值,以便重新开始累加。 -
在
while
循环结束后,将更新后的cur
值添加到list
的末尾。 -
遍历完成后,
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();
}
}