From 95fa60c32cca538aa20115320a093bdee0500409 Mon Sep 17 00:00:00 2001 From: Baturu <45113401+z80160280@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:53:45 -0700 Subject: [PATCH 1/3] =?UTF-8?q?Update=200115.=E4=B8=8D=E5=90=8C=E7=9A=84?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0115.不同的子序列.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/problems/0115.不同的子序列.md b/problems/0115.不同的子序列.md index 1661acf8..e54389aa 100644 --- a/problems/0115.不同的子序列.md +++ b/problems/0115.不同的子序列.md @@ -148,7 +148,22 @@ Java: 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: From 96e0d91f0bb429851e0280bec39b21cafb69eed9 Mon Sep 17 00:00:00 2001 From: youngyangyang04 <826123027@qq.com> Date: Thu, 10 Jun 2021 11:10:41 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=AF=8F=E6=97=A5?= =?UTF-8?q?=E6=B8=A9=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- problems/0739.每日温度.md | 196 ++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 problems/0739.每日温度.md diff --git a/README.md b/README.md index 4fa9100d..daef7c02 100644 --- a/README.md +++ b/README.md @@ -384,9 +384,12 @@ 54. [动态规划:最长回文子序列](./problems/0516.最长回文子序列.md) 55. [动态规划总结篇](./problems/动态规划总结篇.md) - (持续更新中....) +## 单调栈 + +1. [每日温度](./problems/0739.每日温度.md) + ## 图论 ## 十大排序 diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md new file mode 100644 index 00000000..516d068d --- /dev/null +++ b/problems/0739.每日温度.md @@ -0,0 +1,196 @@ + +

+ + + + +

+

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ +# 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 dailyTemperatures(vector& T) { + // 递减栈 + stack st; + vector 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 dailyTemperatures(vector& T) { + stack st; // 递减栈 + vector 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) +
+ From da57f5123bb37017970cad1fc4c0ab19ad7c9dd7 Mon Sep 17 00:00:00 2001 From: youngyangyang04 <826123027@qq.com> Date: Thu, 10 Jun 2021 11:10:56 +0800 Subject: [PATCH 3/3] Update --- problems/哈希表总结.md | 46 +++++++++++++------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/problems/哈希表总结.md b/problems/哈希表总结.md index a578a203..c3fbde2b 100644 --- a/problems/哈希表总结.md +++ b/problems/哈希表总结.md @@ -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(集合) * 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,都是很有考究的。 @@ -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不就完事了。 @@ -56,7 +55,7 @@ ## set作为哈希表 -在[哈希表:两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)中我们给出了什么时候用数组就不行了,需要用set。 +在[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)中我们给出了什么时候用数组就不行了,需要用set。 这道题目没有限制数值的大小,就无法使用数组来做哈希表了。 @@ -67,7 +66,7 @@ 所以此时一样的做映射的话,就可以使用set了。 -关于set,C++ 给提供了如下三种可用的数据结构:(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)) +关于set,C++ 给提供了如下三种可用的数据结构:(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)) * std::set * std::multiset @@ -75,12 +74,12 @@ 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作为哈希表 -在[哈希表:两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)中map正式登场。 +在[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)中map正式登场。 来说一说:使用数组和set来做哈希法的局限。 @@ -89,7 +88,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底 map是一种``的结构,本题可以用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::multimap @@ -97,21 +96,21 @@ C++提供如下三种map::(详情请看[关于哈希表,你该了解这 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)