Merge remote-tracking branch 'origin/master'

This commit is contained in:
KailokFung
2021-06-10 14:08:50 +08:00
4 changed files with 232 additions and 32 deletions

View File

@ -384,9 +384,12 @@
54. [动态规划:最长回文子序列](./problems/0516.最长回文子序列.md) 54. [动态规划:最长回文子序列](./problems/0516.最长回文子序列.md)
55. [动态规划总结篇](./problems/动态规划总结篇.md) 55. [动态规划总结篇](./problems/动态规划总结篇.md)
(持续更新中.... (持续更新中....
## 单调栈
1. [每日温度](./problems/0739.每日温度.md)
## 图论 ## 图论
## 十大排序 ## 十大排序

View File

@ -148,7 +148,22 @@ Java
Python Python
```python
class Solution:
def numDistinct(self, s: str, t: str) -> int:
dp = [[0] * (len(t)+1) for _ in range(len(s)+1)]
for i in range(len(s)):
dp[i][0] = 1
for j in range(1, len(t)):
dp[0][j] = 0
for i in range(1, len(s)+1):
for j in range(1, len(t)+1):
if s[i-1] == t[j-1]:
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
else:
dp[i][j] = dp[i-1][j]
return dp[-1][-1]
```
Go Go

View File

@ -0,0 +1,196 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 739. 每日温度
https://leetcode-cn.com/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++代码如下
```C++
// 版本一
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;
}
};
```
**建议一开始 都把每种情况分析好,不要上来看简短的代码,关键逻辑都被隐藏了**。
精简代码如下:
```C++
// 版本二
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++) {
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
Python
Go
-----------------------
* 作者微信[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>

View File

@ -9,11 +9,10 @@
> 哈希表总结篇如约而至 > 哈希表总结篇如约而至
哈希表系列也是早期讲解的时候没有写总结篇,所以选个周末给补上,毕竟「代码随想录」的系列怎么能没有总结篇呢[机智]。
# 哈希表理论基础 # 哈希表理论基础
在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)中,我们介绍了哈希表的基础理论知识,不同于枯燥的讲解,这里介绍了都是对刷题有帮助的理论知识点。 在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中,我们介绍了哈希表的基础理论知识,不同于枯燥的讲解,这里介绍了都是对刷题有帮助的理论知识点。
**一般来说哈希表都是用来快速判断一个元素是否出现集合里** **一般来说哈希表都是用来快速判断一个元素是否出现集合里**
@ -29,7 +28,7 @@
* set集合 * set集合
* map映射 * map映射
在C++语言中set 和 map 都分别提供了三种数据结构,每种数据结构的底层实现和用途都有所不同,在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)中我给出了详细分析,这一知识点很重要! 在C++语言中set 和 map 都分别提供了三种数据结构,每种数据结构的底层实现和用途都有所不同,在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中我给出了详细分析,这一知识点很重要!
例如什么时候用std::set什么时候用std::multiset什么时候用std::unordered_set都是很有考究的。 例如什么时候用std::set什么时候用std::multiset什么时候用std::unordered_set都是很有考究的。
@ -41,13 +40,13 @@
一些应用场景就是为数组量身定做的。 一些应用场景就是为数组量身定做的。
在[哈希表:有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)中,我们提到了数组就是简单的哈希表,但是数组的大小是受限的! 在[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)中,我们提到了数组就是简单的哈希表,但是数组的大小是受限的!
这道题目包含小写字母,那么使用数组来做哈希最合适不过。 这道题目包含小写字母,那么使用数组来做哈希最合适不过。
在[哈希表:赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)中同样要求只有小写字母,那么就给我们浓浓的暗示,用数组! 在[383.赎金信](https://mp.weixin.qq.com/s/qAXqv--UERmiJNNpuphOUQ)中同样要求只有小写字母,那么就给我们浓浓的暗示,用数组!
本题和[哈希表:有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)很像,[哈希表:有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)是求 字符串a 和 字符串b 是否可以相互组成,在[哈希表:赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)中是求字符串a能否组成字符串b而不用管字符串b 能不能组成字符串a。 本题和[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)很像,[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)是求 字符串a 和 字符串b 是否可以相互组成,在[383.赎金信](https://mp.weixin.qq.com/s/qAXqv--UERmiJNNpuphOUQ)中是求字符串a能否组成字符串b而不用管字符串b 能不能组成字符串a。
一些同学可能想用数组干啥都用map不就完事了。 一些同学可能想用数组干啥都用map不就完事了。
@ -56,7 +55,7 @@
## set作为哈希表 ## set作为哈希表
在[哈希表:两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)中我们给出了什么时候用数组就不行了需要用set。 在[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)中我们给出了什么时候用数组就不行了需要用set。
这道题目没有限制数值的大小,就无法使用数组来做哈希表了。 这道题目没有限制数值的大小,就无法使用数组来做哈希表了。
@ -67,7 +66,7 @@
所以此时一样的做映射的话就可以使用set了。 所以此时一样的做映射的话就可以使用set了。
关于setC++ 给提供了如下三种可用的数据结构:(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA) 关于setC++ 给提供了如下三种可用的数据结构:(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)
* std::set * std::set
* std::multiset * std::multiset
@ -75,12 +74,12 @@
std::set和std::multiset底层实现都是红黑树std::unordered_set的底层实现是哈希 使用unordered_set 读写效率是最高的本题并不需要对数据进行排序而且还不要让数据重复所以选择unordered_set。 std::set和std::multiset底层实现都是红黑树std::unordered_set的底层实现是哈希 使用unordered_set 读写效率是最高的本题并不需要对数据进行排序而且还不要让数据重复所以选择unordered_set。
在[哈希表:快乐数](https://mp.weixin.qq.com/s/G4Q2Zfpfe706gLK7HpZHpA)中我们再次使用了unordered_set来判断一个数是否重复出现过。 在[202.快乐数](https://mp.weixin.qq.com/s/n5q0ujxxrjQS3xuh3dgqBQ)中我们再次使用了unordered_set来判断一个数是否重复出现过。
## map作为哈希表 ## map作为哈希表
在[哈希表:两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)中map正式登场。 在[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)中map正式登场。
来说一说使用数组和set来做哈希法的局限。 来说一说使用数组和set来做哈希法的局限。
@ -89,7 +88,7 @@ std::set和std::multiset底层实现都是红黑树std::unordered_set的底
map是一种`<key, value>`的结构本题可以用key保存数值用value在保存数值所在的下表。所以使用map最为合适。 map是一种`<key, value>`的结构本题可以用key保存数值用value在保存数值所在的下表。所以使用map最为合适。
C++提供如下三种map详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA) C++提供如下三种map详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)
* std::map * std::map
* std::multimap * std::multimap
@ -97,21 +96,21 @@ C++提供如下三种map详情请看[关于哈希表,你该了解这
std::unordered_map 底层实现为哈希std::map 和std::multimap 的底层实现是红黑树。 std::unordered_map 底层实现为哈希std::map 和std::multimap 的底层实现是红黑树。
同理std::map 和std::multimap 的key也是有序的这个问题也经常作为面试题考察对语言容器底层的理解[哈希表:两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)中并不需要key有序选择std::unordered_map 效率更高! 同理std::map 和std::multimap 的key也是有序的这个问题也经常作为面试题考察对语言容器底层的理解[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)中并不需要key有序选择std::unordered_map 效率更高!
在[哈希表:四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)中我们提到了其实需要哈希的地方都能找到map的身影。 在[454.四数相加](https://mp.weixin.qq.com/s/12g_w6RzHuEpFts1pT6BWw)中我们提到了其实需要哈希的地方都能找到map的身影。
本题咋眼一看好像和[18. 四数之](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)[15.三数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)差不多,其实差很多! 本题咋眼一看好像和[18. 四数之](https://mp.weixin.qq.com/s/SBU3THi1Kv6Sar7htqCB2Q)[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)差不多,其实差很多!
**关键差别是本题为四个独立的数组只要找到A[i] + B[j] + C[k] + D[l] = 0就可以不用考虑重复问题而[18. 四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)[15.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)是一个数组集合里找到和为0的组合可就难很多了** **关键差别是本题为四个独立的数组只要找到A[i] + B[j] + C[k] + D[l] = 0就可以不用考虑重复问题而[18. 四数之和](https://mp.weixin.qq.com/s/SBU3THi1Kv6Sar7htqCB2Q)[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)是一个数组集合里找到和为0的组合可就难很多了**
用哈希法解决了两数之和,很多同学会感觉用哈希法也可以解决三数之和,四数之和。 用哈希法解决了两数之和,很多同学会感觉用哈希法也可以解决三数之和,四数之和。
其实是可以解决,但是非常麻烦,需要去重导致代码效率很低。 其实是可以解决,但是非常麻烦,需要去重导致代码效率很低。
在[哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)中我给出了哈希法和双指针两个解法,大家就可以体会到,使用哈希法还是比较麻烦的。 在[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)中我给出了哈希法和双指针两个解法,大家就可以体会到,使用哈希法还是比较麻烦的。
所以[18. 四数之](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)[15.三数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)都推荐使用双指针法! 所以18. 四数之15.三数之和都推荐使用双指针法!
# 总结 # 总结
@ -127,19 +126,6 @@ std::unordered_map 底层实现为哈希std::map 和std::multimap 的底层
## 其他语言版本
Java
Python
Go
----------------------- -----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)