第 36 课
- ★Next Greater Number
「栈」:「后进先出」 的线性数据结构,只对一端(栈顶)进行操作。
「单调栈」 栈内元素满足单调性的栈结构。分为单调递增与单调递减。
**「单调递增栈」**保持栈内元素单调递增,假设当前元素为 x ,若栈顶元素 ≤ x,则将 x 入栈,否则不断弹出栈顶元素,直至栈顶元素 ≤ x。
例 [3, 1, 4, 5, 2, 7] ,其「单调递增栈」入栈结束后,栈中仅保留了 [1, 2, 7],其中 3 比 1 大、4 与 5 比 2 大被弹出,「栈顶元素被弹出,当且仅当栈顶元素 > 当前元素」。
「栈中维护单调性的用途」
以 2 为当前元素,栈顶元素为 5,比 2 大,若将 2 放入栈内,则不满足单调递增,因此将 5 弹出。2 (当前) 和 5 (栈顶) 之间到底有什么更深层的关系呢?
栈顶元素的作用:1、需要后面的事情加以确认;2、为前面的事情提供依据。
2 是 5 右边第一个比 5 小的数。2 弹出 5 后,栈顶变为 4 ,此时 4 仍比 2 大,因此 4 也被弹出,这时可确定 2 是 4 右边第一个比它小的数。4 被弹出后,栈顶变为 1,1 ≤ 2,因此 2 被放入栈。
1 是 2 左边第一个小于等于它的数,当一个数字被放入单调递增栈时,其栈内左边的数是它在原始序列中,左边第一个小于等于它的数。
单调栈的本质在于「每一个数字在原始序列中左 / 右边第一个大于 / 小于它自身的数字」,并且由于每一个数字只会入栈一次且最多出栈一次,因此总的时间复杂度为 O(n)。
「单调递增栈」,我们在一遍扫描中求得每个数字左边第一个小于等于它的数,以及右边第一个小于它的数。另外,若想求得每个数字左边第一个小于它的数,则需要从右往左再扫描一遍数组。而对于「单调递减栈」,只需将上述的「小于」改为「大于」即可。
「单调栈」本质就是「在栈内维护元素单调性」,而其作用则为「O(n) 时间复杂度内求取每个数字在整个数组中左 / 右第一个大于 / 小于它的数」。
★Next Greater Number
给你一个数组,返回一个等长的数组,对应索引存储着下一个更大的元素,如果没有更大的元素,就存 -1。
例如:数组 [2, 1, 2, 4, 3],返回数组 [4, 2, 4, -1, -1]。对每一个元素从左向右看,第一个比他大的元素。
需要注意的关键点:
1、遍历顺序,正序先保存后处理,栈内是待处理元素,当前元素 是处理 栈顶元素 的依据,栈内保存的一般是下标。逆序即时处理当前元素,栈顶元素是依据,栈内保存的一般是元素。
2、是否是严格单调递增(递减)
3、保存的是元素还是下标
from typing import List
class Solution:
def nextGreaterElement(self, nums: List[int]) -> List[int]:
n, q = len(nums), []
ans = [-1] * n # 初始化为 -1
for i, x in enumerate(nums): # 正序遍历 确定的是栈顶元素的状态
while q and x > nums[q[-1]]: # x 是栈顶 top 右边第一个比 top大的数
ans[q.pop()] = x # 循环内处理栈内元素,栈内为非严格单调递减
q.append(i) # 下标 因为栈内元素需要下标确定
# print([nums[i] for i in q])
# for i in range(n - 1, -1, -1): # 逆序遍历 确定的是当前元素的状态
# while q and q[-1] <= nums[i]: q.pop() # 维护严格单调递减栈
# if q: ans[i] = q[-1] # 栈顶元素为当前元素右边下一个更大的数
# q.append(nums[i]) # 元素
# # print(q)
return ans
nums = [2, 1, 2, 4, 3]
s = Solution()
s.nextGreaterElement(nums)
# [4, 2, 4, -1, -1]
class Solution {
public int[] nextGreaterElement(int[] nums) {
int n = nums.length;
Deque<Integer> stack = new ArrayDeque<>();
int[] ans = new int[n];
for (int i = n - 1; i >= 0; i--){ // 逆序 维护单调递减栈
while (!stack.isEmpty() && stack.peek() <= nums[i]){
stack.pop();
}
ans[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return ans;
}
}