Day52 | 单调栈:每日温度&&下一个更大的元素I&&下一个更大元素II

单调栈【基础算法精讲 26】_哔哩哔哩_bilibili

及时去掉无用数据,保证栈中元素有序

739.每日温度

739. 每日温度 - 力扣(LeetCode)

从左往右遍历

每次碰到一个数就先和栈顶元素进行比较,如果比栈顶元素大,那说明我们找到了答案,就记录答案,然后把找到答案的元素出栈

我们这样收集下来,栈里面的元素都是单调递减的

image-20241202175614447

举个例子,如图所示

我们从1往后遍历,栈为空,1入栈,此时栈内为1

碰到4,4大于1,那就把1出栈,然后记录答案,4入栈,栈内为4

碰到3,3小于4, 3入栈,栈内为4,3

碰到5, 5大于3,3出栈,记录答案,栈内为4

栈顶元素变成了4,继续比较,5大于4,4也找到了答案,记录答案,4弹出,5入栈

以此类推

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> res(temperatures.size(),0);
stack<int> st;
for(int i=0;i<temperatures.size();i++)
{
while(!st.empty()&&temperatures[i]>temperatures[st.top()])
{
res[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
return res;
}
};

496.下一个更大元素I

496. 下一个更大元素 I - 力扣(LeetCode)

和上一题思路基本一模一样的,就说一下怎么由第一道题变到第二道题

题意:

第二题的意思是第一个数组里面的数字对应到第二个数组里面的相应位置,在第二个数组里面找它的下一个更大数字

举例:

1
2
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]

第一个数组里面的1,对应第二个数组里面的第一个元素,第一个元素的下一个更大数字就是3,我们返回的就是3

栈还是单调递减的栈,单调栈遍历的是数组2,我们在哪里找下一个更大数字,就遍历哪个数组

第一题第二题区别:

首先要知道nums1是nums2的子集,如果nums1有,但是nums2没有,那说明肯定就是-1,不用管的

那既然知道nums1有的nums2肯定有,那我们就直接在第二个数组里面找下一个更大数,每一个元素都找。

在更新答案的时候,如果当前元素是nums1里面的,那我们才会记录到res,否则的话就直接弹出就行

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> st;
vector<int> res(nums1.size(), -1);
unordered_map<int, int> m;
for (int i = 0; i < nums1.size(); i++)
m[nums1[i]] = i;
for (int i = 0; i < nums2.size(); i++)
{
while (!st.empty() && nums2[i] > nums2[st.top()])
{
if (m.find(nums2[st.top()])!=m.end())
{
int index = m[nums2[st.top()]];
res[index] = nums2[i];
}
st.pop();
}
st.push(i);
}
return res;
}
};

灵神解法:

单调栈存的是元素而不是下标了。

笔者这里其实不是很理解,似懂非懂,大家看看代码随想录的就挺好。

感觉是单调栈又不是单调栈,单调栈遍历的感觉像是数组1,但是也能得到正确的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
vector<int> res(nums1.size(),-1);
unordered_map<int,int> m;
stack<int> st;
for(int i=0;i<nums1.size();i++)
m[nums1[i]]=i;
for(int i=0;i<nums2.size();i++)
{
while(!st.empty()&&nums2[i]>st.top())
{
res[m[st.top()]]=nums2[i];
st.pop();
}
if(m.find(nums2[i])!=m.end())
st.push(nums2[i]);
}
return res;
}
};

503.下一个更大的元素II

503. 下一个更大元素 II - 力扣(LeetCode)

思路:

每日温度基础上,加个取余就行,碰到循环数组类似的题很好用

1
2
输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]

例如这个输入

我们就把他当做两个相同的数组拼起来的就行,实际上我们物理上直接把两个一样的数组拼到一起形成一个新数组和加取余符号效果相同

1
输入: nums = [1,2,3,4,3,   1,2,3,4,3]

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n=nums.size();
vector<int> res(n,-1);
stack<int> st;
for(int i=0;i<2*n;i++)
{
while(!st.empty()&&nums[i%n]>nums[st.top()])
{
res[st.top()]=nums[i%n];
st.pop();
}
st.push(i%n);
}
return res;
}
};