目录:
解题及思路学习
739. 每日温度
https://leetcode.cn/problems/daily-temperatures/
给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0
来代替。
示例 1:
输入:temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
思考:暴力的解法就是两层for循环,遍历当天温度后,再遍历当天温度之后的温度。
暴力解法会超时。时间复杂度是O(n^2)
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> result;
for (int i = 0; i < temperatures.size(); i++) {
int count = 0;
for (int j = i + 1; j < temperatures.size(); j++) {
if (temperatures[j] > temperatures[i]) {
count = j - i;
break;
}
}
result.push_back(count);
}
return result;
}
};
随想录:通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。
单调栈的本质是时间换空间。用一个栈来记录遍历过的元素。单调栈里面存放下标i就行。从栈头到栈尾的元素顺序,这里要使用递增顺序。因为只有递增的时候,栈里要加入一个元素i的时候,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。
即:如果求一个元素右边第一个更大元素,单调栈就是递增的,如果求一个元素右边第一个更小元素,单调栈就是递减的。
使用单调栈主要有三个判断条件。
- 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
// 版本一
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& T) {
// 递增栈
stack<int> st;
vector<int> result(T.size(), 0);
st.push(0);
for (int i = 1; i < T.size(); i++) {
if (T[i] < T[st.top()]) { // 情况一
st.push(i);
} else 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) {
stack<int> st; // 递增栈
vector<int> result(T.size(), 0);
for (int i = 0; i < T.size(); i++) {
while (!st.empty() && T[i] > T[st.top()]) { // 注意栈不能为空
result[st.top()] = i - st.top();
st.pop();
}
st.push(i);
}
return result;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)
496.下一个更大元素 I
https://leetcode.cn/problems/next-greater-element-i/
nums1
中数字 x
的 下一个更大元素 是指 x
在 nums2
中对应位置 右侧 的 第一个 比 x
****大的元素。
给你两个 没有重复元素 的数组 nums1
和 nums2
,下标从 0 开始计数,其中nums1
是 nums2
的子集。
对于每个 0 <= i < nums1.length
,找出满足 nums1[i] == nums2[j]
的下标 j
,并且在 nums2
确定 nums2[j]
的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1
。
返回一个长度为 nums1.length
的数组 **ans
**作为答案,满足 **ans[i]
**是如上所述的 下一个更大元素 。
示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
思考:跟上一题好像差不多。可以分步骤进行
1、遍历nums1中的元素。并找到其在nums2中对应的元素下标j。
2、利用单调栈,找在nums2中下标之后有无更大的元素。若找到,则记录下标, 若没找到,则为-1 (可以初始化为-1.)
随想录:result数组应该和nums1一样大,且都初始化为-1。
注意题目中说是两个没有重复元素 的数组 nums1 和 nums2。
没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
跟我想的不同的点,再与用了一个map来保存nums1的数值。这样就可以快速查找。
// 版本一
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> st;
vector<int> result(nums1.size(), -1);
if (nums1.size() == 0) return result;
unordered_map<int, int> umap; // key:下标元素,value:下标
for (int i = 0; i < nums1.size(); i++) {
umap[nums1[i]] = i;
}
st.push(0);
for (int i = 1; i < nums2.size(); 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) { // 看map里是否存在这个元素
int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下标
result[index] = nums2[i];
}
st.pop();
}
st.push(i);
}
}
return result;
}
};
精简一下的代码,我更喜欢下面风格的代码。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> st;
vector<int> result(nums1.size(), -1);
unordered_map<int, int> umap;
for (int i = 0; i < nums1.size(); i++) umap[nums1[i]] = i;
st.push(0);
for (int i = 1; i < nums2.size(); i++) {
if (nums2[i] <= nums2[st.top()]) st.push(i);
else {
while(!st.empty() && nums2[i] > nums2[st.top()]) {
if (umap.count(nums2[st.top()])) {
int index = umap[nums2[st.top()]];
result[index] = nums2[i];
}
st.pop();
}
st.push(i);
}
}
return result;
}
};
知识点记录
知识点
单调栈。
栈头到栈底的顺序,从小到大,即递增顺序。则找的是第一个比自己大的元素。
如果是递减栈,则是求第一个比自己小的元素。
map.count(val) > 0 判断val是否在map中出现过
个人反思
第二题逻辑有点小绕。查询是否存在,如果再套一个for循环,时间复杂度直接变为n方,这样用一个map来查询,可以降低时间复杂度。