单调栈
单调栈,即栈中存储的元素值单调递增 or 递减,通常用于在一维数组中寻找任意位置元素的右边或者左边第一个更 大 / 小 元素的位置。在遍历过程中,使用单调栈记录遍历过的元素,栈中存放的是元素的下标,而并非元素本身。
739.每日温度
给定一个整数数组 temperatures,表示每天的温度,返回一个数组 answer,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。计算一维数组 temperatures 中每一个元素与右边第一个最大的元素的距离。
使用单调栈存储遍历过元素的下标,在遍历过程中可分为三种情况:当前访问元素大于、小于和等于栈顶元素,对应着不同的处理。
当前访问元素 temperatures[i] > st.top(),说明我们找到了对应 st.top() 为下表的元素右边第一个最大值,temperatures[i]。根据题意要求,结果集中存储的是右边第一个更大元素到该元素的距离,因此 result[st.top()] = i - st.top(),并将栈顶元素出栈。注意,当前访问的元素,可能比栈不止一个元素都大,即它可能是多个元素的右边第一个最大的元素,所以需要使用一个循环进行遍历。直至不符合要求或者栈空,然后将当前元素压栈。从上述操作可以看出,栈顶元素为即将要加入结果集的元素的下标,当前访问的元素为可能符合要求的元素。
当前访问元素 temperatures[i] < st.top(),说明当前元素不符合要求,直接将当前元素压栈,相等情况同理。
class Solution {
public:
vector<int> dailyTemperatures(vector<int> &T) {
int len = T.size();
auto result = vector<int> (len, 0);
stack<int> st;
st.push(0);
for (int i = 1; i < len; ++i) {
if (T[i] <= T[st.top()]) st.push(i);
else {
while (!st.empty() && T[i] > T[st.top()]) {
result[st.top()] = i - st.top();
st.pop();
}
st.push(i);
}
}
return result;
}
};
下面是精简过后的代码。
class Solution {
public:
vector<int> dailyTemperatures(vector<int> &T) {
int len = T.size();
vector<int> result(len, 0);
stack<int> st;
for (int i = 0; i < len; ++i) {
while (!st.empty() && T[i] > T[st.top()]) {
result[st.top()] = i - st.top();
st.pop();
}
st.push(i);
}
return result;
}
};
496.下一个更大的元素Ⅰ
题干过于复杂,不在此赘述。本题为单调栈经典题目,设有一些变化。数组 nums1 是 nums2 的子集,寻找 num1 中的元素,在 nums2 中右边第一个更大的元素。
首先是暴力解法,居然通过了,嵌套三个循环,第一个循环用于在 nums1 中挑选元素,第二个循环用于在 nums2 中寻找给定元素的位置,第三个循环用于返回给定元素右起第一个更大的值。在实现上是三层嵌套循环,但是本质上为两层嵌套循环,第三层循环只执行了一次。
class Solution {
public:
vector<int> nextGreaterElement(vector<int> &nums1, vector<int> &nums2) {
int len1 = nums1.size(), len2 = nums2.size();
vector<int> result(len1, -1);
for (int i = 0; i < len1; ++i) {
for (int j = 0; j < len2; ++j) {
if (nums2[j] != nums1[i]) continue;
for (int k = j + 1; k < len2; ++k) {
if (nums2[k] > nums1[i]) {
result[i] = nums2[k];
break;
}
}
break;
}
}
return result;
}
};
单调栈解法,nums1 是 nums2 的子集,那么只要找到 nums2 中所有元素右边第一个更大的元素的值,然后判断该元素在不在 nums1 中,如果在将这个更大的值添加进结果集即可。问题转化为单调栈 + 哈希映射的使用。下面的代码可以如 739.每日温度 进行精简。
class Solution {
public:
vector<int> nextGreaterElement(vector<int> &nums1, vector<int> &nums2) {
int len1 = nums1.size(), len2 = nums2.size();
vector<int> result(len1, -1);
if (len1 == 0) return result;
unordered_map<int, int> umap;
for (int i = 0; i < len1; ++i) umap[nums1[i]] = i;
stack<int> st;
st.push(0);
for (int i = 1; i < len2; ++i) {
if (nums2[i] < nums2[st.top()]) st.push(i);
else if (nums2[i] == nums2[st.top()]) st.push(i);
else {
while (!st.empty() && nums2[i] > nums2[st.top()]) {
if (umap.count(nums2[st.top()]) > 0) {
int index = umap[nums2[st.top()]];
result[index] = nums2[i];
}
st.pop();
}
st.push(i);
}
}
return result;
}
};