单调栈是一种特殊的栈,用于解决一类特定的问题,通常涉及到处理数组或字符串中元素的顺序和大小关系。在单调栈中,栈内元素保持严格的单调递增或递减顺序。这种数据结构在解决如最大矩形面积、下一个更大元素、最长有效括号等问题时特别有用。
基本概念
-
单调增栈:栈内元素从栈底到栈顶单调递增。
-
单调减栈:栈内元素从栈底到栈顶单调递减。
使用场景
-
寻找数组中元素的下一个更大(或更小)元素。
-
计算数组中的某个区间内满足特定条件的最大值或最小值。
-
解决一些涉及区间、序列问题。
单调栈的思路:
一遍遍历,通常栈中存放的元素是按要求求得结果或者暂时没有求得结果的
维护栈的单调性:单调栈内的元素保持严格的递增或递减顺序。这意味着在任何时候,你可以迅速知道栈中元素相对于新加入元素的大小关系。单调递增栈用于找出元素左侧或右侧第一个小于该元素的值,而单调递减栈用于找出元素左侧或右侧第一个大于该元素的值。
处理新元素:当一个新元素需要加入栈时,栈会先弹出不满足单调性的栈顶元素,直到栈顶元素满足单调性为止。这一过程可以帮助找到每个元素左侧或右侧的第一个小于或大于它的元素。
记录相关信息:在弹出元素时,通常会记录一些信息,如弹出元素的索引或值,这对于解决某些问题是必要的。例如,在寻找每个元素左侧第一个小于它的元素时,可以在弹出元素时记录这些信息。
时间复杂度:单调栈的一个重要特点是它的时间复杂度通常为 O(n),因为每个元素最多被压入和弹出栈一次。
以下是使用单调栈解决问题的一个典型例子:打印出数组中每个元素右边第一个比它大的元素 否则返回-1:
import java.util.Stack;
public class MonotonicStack {
public static int[] nextGreaterElement(int[] nums) {
int[] result = new int[nums.length];
Stack<Integer> stack = new Stack<>();//定义单调栈
for (int i = nums.length - 1; i >= 0; i--) {
// 维持栈的单调性
while (!stack.isEmpty() && stack.peek() <= nums[i]) {
stack.pop();
}
// 栈为空表示右边没有更大的元素
result[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return result;
}
public static void main(String[] args) {
int[] nums = {2, 1, 2, 4, 3};
int[] res = nextGreaterElement(nums);
for (int re : res) {
System.out.print(re + " ");
}
}
}
2024.1.5 每日一题 1944. 队列中可以看到的人数
这道题还是很经典的,虽然难度为困难但是用单调栈的思想去做并不难
有 n
个人排成一个队列,从左到右 编号为 0
到 n - 1
。给你以一个整数数组 heights
,每个整数 互不相同,heights[i]
表示第 i
个人的高度。
一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人 矮 。更正式的,第 i
个人能看到第 j
个人的条件是 i < j
且 min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], ..., heights[j-1])
。
请你返回一个长度为 n
的数组 answer
,其中 answer[i]
是第 i
个人在他右侧队列中能 看到 的 人数 。
class Solution:
def canSeePersonsCount(self, heights: List[int]) -> List[int]:
result = [0] * len(heights) # 初始化结果数组为0
stack = [] # 单调栈
for i in range(len(heights)):
# 维持栈的单调递减性
while stack and heights[i] > heights[stack[-1]]:
# 弹出栈顶元素,每弹出一个元素,该元素就多看到一个人(当前元素)
index = stack.pop()
result[index] += 1
# 如果栈不为空,栈顶元素可以看到当前元素
if stack:
result[stack[-1]] += 1
# 将当前元素索引入栈
stack.append(i)
return result
经典例题
入门:
每日温度 739.每日温度
下⼀个更大元素1 496. 下一个更大元素
下⼀个更大元素2 503.下一个更大元素2
下一个更大元素3 556. 下一个更大元素3
更多练习
-
(经典中的经典)接雨水 42.接雨水
-
商品折扣后的最终价格 1475. 商品折扣后的最终价格
-
股票价格跨度 901. 股票价格跨度
-
链表中的下一个更大节点 1019. 链表中的下一个更大节点
-
柱状图中最大的矩形 84.柱形图中最大的矩形
-
美丽塔 2865.美丽塔