栈
最近相关性;用stack。洋葱性。
20. 有效的括号
155. 最小栈
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.add(val);
if (minStack.isEmpty() || val <= minStack.peek()) {
minStack.add(val);
}
}
public void pop() {
int val = stack.pop();
if (val == minStack.peek()) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
单调栈
定义:单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。 栈内元素单调递增或单调递减,这个定义很重要!!!
单调栈用途不太广泛,只处理一种典型的问题,叫做 Next Greater Element。
给你一个数组 [2,1,2,4,3],你返回数组 [4,2,4,-1,-1]。
解释:第一个 2 后面比 2 大的数是 4; 1 后面比 1 大的数是 2;第二个 2 后面比 2 大的数是 4; 4 后面没有比 4 大的数,填 -1;3 后面没有比 3 大的数,填 -1。
- 这道题的暴力解法很好想到,就是对每个元素后面都进行扫描,找到第一个更大的元素就行了。但是暴力解法的时间复杂度是 O(n^2)。
- 这个问题可以这样抽象思考:把数组的元素想象成并列站立的人,元素大小想象成人的身高。这些人面对你站成一列,如何求元素「2」的 Next Greater Number 呢?很简单,如果能够看到元素「2」,那么他后面可见的第一个人就是「2」的 Next Greater Number,因为比「2」小的元素身高不够,都被「2」挡住了,第一个露出来的就是答案。 它的代码,注释改的更好理解。
vector<int> nextGreaterElement(vector<int>& nums) {
vector<int> ans(nums.size()); // 存放答案的数组
stack<int> s; // 要维持一个 倒序的栈。 底部大,上面小。
for (int i = nums.size() - 1; i >= 0; i--) { // 倒着往栈里放
while (!s.empty() && s.top() <= nums[i]) { // 判定个子高矮。。判断扫描到当前的身高与,
s.pop(); // 矮个起开,反正也被挡着了。。。
}
ans[i] = s.empty() ? -1 : s.top(); // 这个元素身后的第一个高个
s.push(nums[i]); // 进队,接受之后的身高判定吧!
}
return ans;
}
O(N)复杂度,每个元素都被push,而最多会被 pop 一次,没有任何冗余操作。所以总的计算规模是和元素规模 n 成正比的,也就是 O(n) 的复杂度。
给你一个数组 T = [73, 74, 75, 71, 69, 72, 76, 73],这个数组存放的是近几天的天气气温(这气温是铁板烧?不是的,这里用的华氏度)。你返回一个数组,计算:对于每一天,你还要至少等多少天才能等到一个更暖和的气温;如果等不到那一天,填 0 。
举例:给你 T = [73, 74, 75, 71, 69, 72, 76, 73],你返回 [1, 1, 4, 2 , 1, 1, 0, 0]。
// 小试牛刀,Java版本。
public int[] moreWarm(int[] nums) {
if (nums.length <= 1) return nums;
int n = nums.length;
int[] ans = new int[n];
Stack<Integer> stack = new Stack<>(); // stack 存放的是索引。 不是数值。
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[i] < nums[stack.peek()] ) {
stack.pop();
}
ans[i] = stack.isEmpty() ? 0 : stack.peek() - i; // 间距
stack.push(i); // 加入索引。
}
return ans; // answer
}
239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
// labuladong c++解法,本质还是 类似递增栈,这道题,只不过是换成了,递减的队列。
class MonotonicQueue {
private:
deque<int> data;
public:
void push(int n) {
while (!data.empty() && data.back() < n)
data.pop_back();
data.push_back(n);
}
int max() { return data.front(); }
void pop(int n) {
if (!data.empty() && data.front() == n)
data.pop_front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MonotonicQueue window;
vector<int> res;
for (int i = 0; i < nums.size(); i++) {
if (i < k - 1) { //先填满窗口的前 k - 1
window.push(nums[i]);
} else { // 窗口向前滑动
window.push(nums[i]);
res.push_back(window.max());
window.pop(nums[i - k + 1]);
}
}
return res;
}
public int[] maxSlidingWindow(int[] nums, int k) {
int[] ans = new int[nums.length - k + 1];
if (nums.length <= 1) return nums;
Deque<Integer> queue = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
while (!queue.isEmpty() && queue.peekLast() < nums[i]) {
queue.pollLast();
}
queue.addLast(i);
if (queue.peekFirst() <= i - k) {
queue.pollFirst();
}
if (i >= k - 1) {
ans[i - k + 1] = nums[queue.peekFirst()];
}
}
}
84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3] 输出:10 解释:最大的矩形为图中红色区域,面积为 10
class Solution {
public int largestRectangleArea(int[] heights) {
int len = heights.length;
int area = 0;
if (len == 1) {
return heights[0];
}
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < len; i++) {
// 这个while 就是 当 我现在的 元素 比 stack 的栈顶 元素要小 那么久 弹出栈,,,但是 还有可能不止一个
// 比我现在这个元素小。。那么就需要用while 把他们都弹出栈。并且,这个过程去计算,Area
while (!stack.isEmpty() && heights[stack.peekLast()] > heights[i]) {
/*开始了*/
int height = heights[stack.removeLast()];
int width;
if (stack.isEmpty()) {
width = i;
} else {
width = i - stack.peekLast() - 1;
}
area = Math.max(area, width * height);
}
stack.addLast(i);
}
while (!stack.isEmpty()) {
int height = heights[stack.removeLast()];
int width;
if (stack.isEmpty()) {
width = len;
}else {
width = len - stack.peekLast() - 1;
}
area = Math.max(area, width * height);
}
return area ;
}
}
以下列出了单调栈的问题,供大家参考。
序号 题目 题解
1 42. 接雨水(困难) 暴力解法、优化、双指针、单调栈
2 739. 每日温度(中等) 暴力解法 + 单调栈
3 496. 下一个更大元素 I(简单) 暴力解法、单调栈
4 316. 去除重复字母(困难) 栈 + 哨兵技巧(Java、C++、Python)
5 901. 股票价格跨度(中等) 「力扣」第 901 题:股票价格跨度(单调栈)
6 402. 移掉K位数字
7 581. 最短无序连续子数组
这里感谢 @chwma 朋友提供资料。 // 源自leetcode