leetcode-master/problems/0739.每日温度.md

408 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://programmercarl.com/other/xunlianying.html" target="_blank">
<img src="../pics/训练营.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 739. 每日温度
[力扣题目链接](https://leetcode.cn/problems/daily-temperatures/)
请根据每日 气温 列表重新生成一个列表。对应位置的输出为要想观测到更高的气温至少需要等待的天数。如果气温在这之后都不会升高请在该位置用 0 来代替。
例如给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
## 思路
首先想到的当然是暴力解法两层for循环把至少需要等待的天数就搜出来了。时间复杂度是O(n^2)
那么接下来在来看看使用单调栈的解法。
那有同学就问了,我怎么能想到用单调栈呢? 什么时候用单调栈呢?
**通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了**
时间复杂度为O(n)。
例如本题其实就是找找到一个元素右边第一个比自己大的元素。
此时就应该想到用单调栈了。
那么单调栈的原理是什么呢为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是只需要遍历一次。
在使用单调栈的时候首先要明确如下几点:
1. 单调栈里存放的元素是什么?
单调栈里只需要存放元素的下标i就可以了如果需要使用对应的元素直接T[i]就可以获取。
2. 单调栈里元素是递增呢? 还是递减呢?
**注意一下顺序为 从栈头到栈底的顺序**,因为单纯的说从左到右或者从前到后,不说栈头朝哪个方向的话,大家一定会越看越懵。
这里我们要使用递增循序再强调一下是指从栈头到栈底的顺序因为只有递增的时候加入一个元素i才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。
文字描述理解起来有点费劲,接下来我画了一系列的图,来讲解单调栈的工作过程。
使用单调栈主要有三个判断条件。
* 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
* 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
* 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
**把这三种情况分析清楚了,也就理解透彻了**
接下来我们用temperatures = [73, 74, 75, 71, 71, 72, 76, 73]为例来逐步分析,输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
首先先将第一个遍历元素加入单调栈
![739.每日温度1](https://img-blog.csdnimg.cn/20210219124434172.jpg)
加入T[1] = 74因为T[1] > T[0]当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况而我们要保持一个递增单调栈从栈头到栈底所以将T[0]弹出T[1]加入此时result数组可以记录了result[0] = 1即T[0]右面第一个比T[0]大的元素是T[1]。
![739.每日温度2](https://img-blog.csdnimg.cn/20210219124504299.jpg)
加入T[2]同理T[1]弹出
![739.每日温度3](https://img-blog.csdnimg.cn/20210219124527361.jpg)
加入T[3]T[3] < T[2] 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况加T[3]加入单调栈
![739.每日温度4](https://img-blog.csdnimg.cn/20210219124610761.jpg)
加入T[4]T[4] == T[3] 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况此时依然要加入栈不用计算距离因为我们要求的是右面第一个大于本元素的位置而不是大于等于
![739.每日温度5](https://img-blog.csdnimg.cn/20210219124633444.jpg)
加入T[5]T[5] > T[4] 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况将T[4]弹出同时计算距离更新result
![739.每日温度6](https://img-blog.csdnimg.cn/20210219124700567.jpg)
T[4]弹出之后, T[5] > T[3] 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况将T[3]继续弹出同时计算距离更新result
![739.每日温度7](https://img-blog.csdnimg.cn/20210219124726613.jpg)
直到发现T[5]小于T[st.top()]终止弹出将T[5]加入单调栈
![739.每日温度8](https://img-blog.csdnimg.cn/20210219124807715.jpg)
加入T[6]同理需要将栈里的T[5]T[2]弹出
![739.每日温度9](https://img-blog.csdnimg.cn/2021021912483374.jpg)
同理,继续弹出
![739.每日温度10](https://img-blog.csdnimg.cn/2021021912490098.jpg)
此时栈里只剩下了T[6]
![739.每日温度11](https://img-blog.csdnimg.cn/20210219124930156.jpg)
加入T[7] T[7] < T[6] 直接入栈这就是最后的情况result数组也更新完了
![739.每日温度12](https://img-blog.csdnimg.cn/20210219124957216.jpg)
此时有同学可能就疑惑了那result[6] , result[7]怎么没更新啊元素也一直在栈里
其实定义result数组的时候就应该直接初始化为0如果result没有更新说明这个元素右面没有更大的了也就是为0
以上在图解的时候已经把这三种情况都做了详细的分析
* 情况一当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
* 情况二当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
* 情况三当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
C++代码如下
```CPP
// 版本一
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;
}
};
```
**建议一开始 都把每种情况分析好,不要上来看简短的代码,关键逻辑都被隐藏了**
精简代码如下
```CPP
// 版本二
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)
精简的代码是直接把情况一二三都合并到了一起其实这种代码精简是精简但思路不是很清晰
建议大家把情况一二三想清楚了先写出版本一的代码然后在其基础上在做精简
## 其他语言版本
Java
```java
class Solution {
// 版本 1
public int[] dailyTemperatures(int[] temperatures) {
int lens=temperatures.length;
int []res=new int[lens];
/**
如果当前遍历的元素 大于栈顶元素,表示 栈顶元素的 右边的最大的元素就是 当前遍历的元素,
所以弹出 栈顶元素,并记录
如果栈不空的话,还要考虑新的栈顶与当前元素的大小关系
否则的话,可以直接入栈。
注意,单调栈里 加入的元素是 下标。
*/
Deque<Integer> stack=new LinkedList<>();
stack.push(0);
for(int i=1;i<lens;i++){
if(temperatures[i]<=temperatures[stack.peek()]){
stack.push(i);
}else{
while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
res[stack.peek()]=i-stack.peek();
stack.pop();
}
stack.push(i);
}
}
return res;
}
//--------这 是一条分界线
// 版本 2
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int lens=temperatures.length;
int []res=new int[lens];
Deque<Integer> stack=new LinkedList<>();
for(int i=0;i<lens;i++){
while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
res[stack.peek()]=i-stack.peek();
stack.pop();
}
stack.push(i);
}
return res;
}
}
}
```
Python
```python
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
answer = [0]*len(temperatures)
stack = [0]
for i in range(1,len(temperatures)):
# 情况一和情况二
if temperatures[i]<=temperatures[stack[-1]]:
stack.append(i)
# 情况三
else:
while len(stack) != 0 and temperatures[i]>temperatures[stack[-1]]:
answer[stack[-1]]=i-stack[-1]
stack.pop()
stack.append(i)
return answer
```
Go
> 暴力法
```go
func dailyTemperatures(t []int) []int {
var res []int
for i := 0; i < len(t)-1; i++ {
j := i + 1
for ; j < len(t); j++ {
// 如果之后出现更高,说明找到了
if t[j] > t[i] {
res = append(res, j-i)
break
}
}
// 找完了都没找到
if j == len(t) {
res = append(res, 0)
}
}
// 最后一个肯定是 0
return append(res, 0)
}
```
> 单调栈法(未精简版本)
```go
func dailyTemperatures(temperatures []int) []int {
res := make([]int, len(temperatures))
// 初始化栈顶元素为第一个下标索引0
stack := []int{0}
for i := 1; i < len(temperatures); i++ {
top := stack[len(stack)-1]
if temperatures[i] < temperatures[top] {
stack = append(stack, i)
} else if temperatures[i] == temperatures[top] {
stack = append(stack, i)
} else {
for len(stack) != 0 && temperatures[i] > temperatures[top] {
res[top] = i - top
stack = stack[:len(stack)-1]
if len(stack) != 0 {
top = stack[len(stack)-1]
}
}
stack = append(stack, i)
}
}
return res
}
```
> 单调栈法(精简版本)
```go
// 单调递减栈
func dailyTemperatures(num []int) []int {
ans := make([]int, len(num))
stack := []int{}
for i, v := range num {
// 栈不空,且当前遍历元素 v 破坏了栈的单调性
for len(stack) != 0 && v > num[stack[len(stack)-1]] {
// pop
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
ans[top] = i - top
}
stack = append(stack, i)
}
return ans
}
```
JavaScript
```javascript
// 版本一
var dailyTemperatures = function(temperatures) {
const n = temperatures.length;
const res = Array(n).fill(0);
const stack = []; // 递增栈:用于存储元素右面第一个比他大的元素下标
stack.push(0);
for (let i = 1; i < n; i++) {
// 栈顶元素
const top = stack[stack.length - 1];
if (temperatures[i] < temperatures[top]) {
stack.push(i);
} else if (temperatures[i] === temperatures[top]) {
stack.push(i);
} else {
while (stack.length && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const top = stack.pop();
res[top] = i - top;
}
stack.push(i);
}
}
return res;
};
// 版本二
var dailyTemperatures = function(temperatures) {
const n = temperatures.length;
const res = Array(n).fill(0);
const stack = []; // 递增栈:用于存储元素右面第一个比他大的元素下标
stack.push(0);
for (let i = 1; i < n; i++) {
while (stack.length && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const top = stack.pop();
res[top] = i - top;
}
stack.push(i);
}
return res;
};
```
TypeScript:
> 精简版:
```typescript
function dailyTemperatures(temperatures: number[]): number[] {
const length: number = temperatures.length;
const stack: number[] = [];
const resArr: number[] = new Array(length).fill(0);
stack.push(0);
for (let i = 1; i < length; i++) {
let top = stack[stack.length - 1];
while (
stack.length > 0 &&
temperatures[top] < temperatures[i]
) {
resArr[top] = i - top;
stack.pop();
top = stack[stack.length - 1];
}
stack.push(i);
}
return resArr;
};
```
<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a>