栈(stack)是一种特殊的数据结构,但也是一种容易理解的数据结构,它的特点就是先进后出,生活中有很多栈的例子,比如装乒乓球的直筒,最先进入的球到达桶底,然后一个一个进入,最后进入的球在出桶的时候是第一出来,最先进去的是最后一个出来。本文所提到的单调栈其实就是在普通栈的基础上加上了单调的特性,栈内元素保持单调递增或者单调递减的特性。
一、 基础案例
(1)数组内下一个更大的元素
给你一个数组nums,请返回一个等长的数组,这个等长数组对应于nums的相同位置存储着下一个更大元素,如果没有更大元素,请存储数值-1。假设nums的每个元素都不为负数。
案例:
比如输入数组为nums = [2,1,3,4,2],那么返回数组[3,3,4,-1,-1]
解释:
数组nums的第一个元素2的下一个更大元素是3,第二个元素1的下一个更大元素是3,第三个元素3的下一个更大元素是4,第四个元素4没有下一个更大元素,第五个元素2同样没有下一个更大元素。
思路:利用单调栈 建立栈stack<int> a; 用来存储下一个最大的元素。
从末尾向前遍历nums,当 a不为空,且 nums[i] >= a.top() 要压出栈顶的元素,直至 a为空或者 nums[i] < a.top(),
class Solution {
public:
vector<int> nextGreaterNum(vector<int>& nums) {
int size = nums.size();
stack<int> a;
vector<int> res(size, 0);
for(int i = size - 1; i >= 0; i--)
{
while(!a.empty()&&nums[i] >= a.top()) //a 的栈顶元素,即为当前nums[i] 后面第一个大于nums[i]的元素
{
a.pop();
}
res[i] = a.empty()? -1: a.top();
a.push(nums[i]);
}
return res;
}
};
(2) 单调栈的变形利用
此时,单调栈存储的是 下一个最大值的索引值
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int size = temperatures.size();
stack<int> a; //此时,单调栈存储的是 下一个最大值的索引值
vector<int> res(size, 0);
for(int i = size - 1; i >= 0; i--)
{
while(!a.empty() && temperatures[i]>= temperatures[a.top()])
{
a.pop();
}
res[i] = a.empty()? 0 : a.top() - i;
a.push(i);
}
return res;
}
};
leetcode 496. 下一个更大元素 I
本题中,是两个数组,其中nums1是nums2的子数组,因此,首先计算出nums2中每一个元素的后一个最大元素,在利用map将对应关系保存,然后,在根据nums1数组中元素去找对应的后一个最大元素。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> a;
unordered_map<int, int> maptemp;//存在nums2中每一个元素与后一个较大元素的对应关系
for(int i = nums2.size() - 1; i >= 0; i--)
{
while(!a.empty() && nums2[i] >= a.top())
{
a.pop();
}
maptemp[nums2[i]] = a.empty()? -1 : a.top();
a.push(nums2[i]);
}
vector<int> res(nums1.size(), 0);
for(int j = 0; j < nums1.size(); j++)
{
res[j] = maptemp[nums1[j]];
}
return res;
}
};
本题的难点是:循环数组。在数据结构中,利用模运算模拟模拟环状数据结构。
因为要考虑环状数组,首先想到的是构建新的nums是原nums中元素可以循环起来,但是这样会耗费空间。用取模操作。
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int size = nums.size();
stack<int> a;
vector<int> res(size, 0);
for(int i = (2*size - 1); i >= 0; i--)//此时相当于将nums边长构成循环数组
{
while(!a.empty() && nums[i%size] >= a.top())
{
a.pop();
}
res[i%size] = a.empty()? -1:a.top();
a.push(nums[i%size]);
}
return res;
}
};