mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-09 03:34:02 +08:00
Merge branch 'youngyangyang04:master' into master
This commit is contained in:
@ -131,6 +131,7 @@
|
|||||||
7. [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw)
|
7. [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw)
|
||||||
8. [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg)
|
8. [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg)
|
||||||
9. [秋招和提前批都越来越提前了....](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ)
|
9. [秋招和提前批都越来越提前了....](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ)
|
||||||
|
10. [你的简历里「专业技能」写的够专业么?](https://mp.weixin.qq.com/s/bp6y-e5FVN28H9qc8J9zrg)
|
||||||
|
|
||||||
|
|
||||||
## 数组
|
## 数组
|
||||||
@ -395,7 +396,8 @@
|
|||||||
|
|
||||||
## 单调栈
|
## 单调栈
|
||||||
|
|
||||||
1. [每日温度](./problems/0739.每日温度.md)
|
1. [单调栈:每日温度](./problems/0739.每日温度.md)
|
||||||
|
2. [单调栈:下一个更大元素I](./problems/0496.下一个更大元素I.md)
|
||||||
|
|
||||||
## 图论
|
## 图论
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ candidates 中的每个数字在每个组合中只能使用一次。
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
这道题目和[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)如下区别:
|
这道题目和[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)如下区别:
|
||||||
|
|
||||||
1. 本题candidates 中的每个数字在每个组合中只能使用一次。
|
1. 本题candidates 中的每个数字在每个组合中只能使用一次。
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
本题相对于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)还是难了不少。
|
本题相对于[55.跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)还是难了不少。
|
||||||
|
|
||||||
但思路是相似的,还是要看最大覆盖范围。
|
但思路是相似的,还是要看最大覆盖范围。
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ public:
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
相信大家可以发现,这道题目相当于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不止一点。
|
相信大家可以发现,这道题目相当于[55.跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不止一点。
|
||||||
|
|
||||||
但代码又十分简单,贪心就是这么巧妙。
|
但代码又十分简单,贪心就是这么巧妙。
|
||||||
|
|
||||||
@ -228,11 +228,6 @@ var jump = function(nums) {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
/*
|
|
||||||
dp[i]表示从起点到当前位置的最小跳跃次数
|
|
||||||
dp[i]=min(dp[j]+1,dp[i]) 表示从j位置用一步跳跃到当前位置,这个j位置可能有很多个,却最小一个就可以
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +27,10 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
此时我们已经学习了[组合问题](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)、[切割问题](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)和[子集问题](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA),接下来看一看排列问题。
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
|
此时我们已经学习了[77.组合问题](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)、 [131.分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)和[78.子集问题](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA),接下来看一看排列问题。
|
||||||
|
|
||||||
相信这个排列问题就算是让你用for循环暴力把结果搜索出来,这个暴力也不是很好写。
|
相信这个排列问题就算是让你用for循环暴力把结果搜索出来,这个暴力也不是很好写。
|
||||||
|
|
||||||
@ -81,7 +84,7 @@ if (path.size() == nums.size()) {
|
|||||||
|
|
||||||
* 单层搜索的逻辑
|
* 单层搜索的逻辑
|
||||||
|
|
||||||
这里和[组合问题](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)、[切割问题](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)和[子集问题](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)最大的不同就是for循环里不用startIndex了。
|
这里和[77.组合问题](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)、[131.切割问题](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)和[78.子集问题](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)最大的不同就是for循环里不用startIndex了。
|
||||||
|
|
||||||
因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。
|
因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
* -10 <= nums[i] <= 10
|
* -10 <= nums[i] <= 10
|
||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
这道题目和[回溯算法:排列问题!](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。
|
这道题目和[回溯算法:排列问题!](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
都知道n皇后问题是回溯算法解决的经典问题,但是用回溯解决多了组合、切割、子集、排列问题之后,遇到这种二位矩阵还会有点不知所措。
|
都知道n皇后问题是回溯算法解决的经典问题,但是用回溯解决多了组合、切割、子集、排列问题之后,遇到这种二位矩阵还会有点不知所措。
|
||||||
|
|
||||||
首先来看一下皇后们的约束条件:
|
首先来看一下皇后们的约束条件:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
看完了这篇[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),再来看看如何求最小深度。
|
看完了这篇[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),再来看看如何求最小深度。
|
||||||
|
|
||||||
直觉上好像和求最大深度差不多,其实还是差不少的。
|
直觉上好像和求最大深度差不多,其实还是差不少的。
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ public:
|
|||||||
|
|
||||||
## 迭代法
|
## 迭代法
|
||||||
|
|
||||||
相对于[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),本题还可以使用层序遍历的方式来解决,思路是一样的。
|
相对于[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),本题还可以使用层序遍历的方式来解决,思路是一样的。
|
||||||
|
|
||||||
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
|
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
|
||||||
|
|
||||||
|
@ -291,6 +291,27 @@ func wordBreak(s string,wordDict []string) bool {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Javascript:
|
||||||
|
```javascript
|
||||||
|
const wordBreak = (s, wordDict) => {
|
||||||
|
|
||||||
|
let dp = Array(s.length + 1).fill(false);
|
||||||
|
dp[0] = true;
|
||||||
|
|
||||||
|
for(let i = 0; i <= s.length; i++){
|
||||||
|
for(let j = 0; j < wordDict.length; j++) {
|
||||||
|
if(i >= wordDict[j].length) {
|
||||||
|
if(s.slice(i - wordDict[j].length, i) === wordDict[j] && dp[i - wordDict[j].length]) {
|
||||||
|
dp[i] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[s.length];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -26,27 +26,26 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
|
|||||||
|
|
||||||
|
|
||||||
示例 1:
|
示例 1:
|
||||||
输入: ["2", "1", "+", "3", " * "]
|
* 输入: ["2", "1", "+", "3", " * "]
|
||||||
输出: 9
|
* 输出: 9
|
||||||
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
|
* 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
|
||||||
|
|
||||||
示例 2:
|
示例 2:
|
||||||
输入: ["4", "13", "5", "/", "+"]
|
* 输入: ["4", "13", "5", "/", "+"]
|
||||||
输出: 6
|
* 输出: 6
|
||||||
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
|
* 解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
|
||||||
|
|
||||||
示例 3:
|
示例 3:
|
||||||
输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"]
|
* 输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"]
|
||||||
输出: 22
|
* 输出: 22
|
||||||
解释:
|
* 解释:该算式转化为常见的中缀算术表达式为:
|
||||||
该算式转化为常见的中缀算术表达式为:
|
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
|
||||||
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
|
= ((10 * (6 / (12 * -11))) + 17) + 5
|
||||||
= ((10 * (6 / (12 * -11))) + 17) + 5
|
= ((10 * (6 / -132)) + 17) + 5
|
||||||
= ((10 * (6 / -132)) + 17) + 5
|
= ((10 * 0) + 17) + 5
|
||||||
= ((10 * 0) + 17) + 5
|
= (0 + 17) + 5
|
||||||
= (0 + 17) + 5
|
= 17 + 5
|
||||||
= 17 + 5
|
= 22
|
||||||
= 22
|
|
||||||
|
|
||||||
|
|
||||||
逆波兰表达式:是一种后缀表达式,所谓后缀就是指算符写在后面。
|
逆波兰表达式:是一种后缀表达式,所谓后缀就是指算符写在后面。
|
||||||
@ -63,20 +62,20 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
|
|||||||
|
|
||||||
# 思路
|
# 思路
|
||||||
|
|
||||||
在上一篇文章中[栈与队列:匹配问题都是栈的强项](https://mp.weixin.qq.com/s/eynAEbUbZoAWrk0ZlEugqg)提到了 递归就是用栈来实现的。
|
在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)提到了 递归就是用栈来实现的。
|
||||||
|
|
||||||
所以**栈与递归之间在某种程度上是可以转换的!**这一点我们在后续讲解二叉树的时候,会更详细的讲解到。
|
所以**栈与递归之间在某种程度上是可以转换的!** 这一点我们在后续讲解二叉树的时候,会更详细的讲解到。
|
||||||
|
|
||||||
那么来看一下本题,**其实逆波兰表达式相当于是二叉树中的后序遍历**。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。
|
那么来看一下本题,**其实逆波兰表达式相当于是二叉树中的后序遍历**。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。
|
||||||
|
|
||||||
但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后续遍历的方式把二叉树序列化了,就可以了。
|
但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后续遍历的方式把二叉树序列化了,就可以了。
|
||||||
|
|
||||||
在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[栈与队列:匹配问题都是栈的强项](https://mp.weixin.qq.com/s/eynAEbUbZoAWrk0ZlEugqg)中的对对碰游戏是不是就非常像了。**
|
在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)中的对对碰游戏是不是就非常像了。**
|
||||||
|
|
||||||
如动画所示:
|
如动画所示:
|
||||||

|

|
||||||
|
|
||||||
相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/eynAEbUbZoAWrk0ZlEugqg)是差不错的,只不过本题不要相邻元素做消除了,而是做运算!
|
相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)是差不错的,只不过本题不要相邻元素做消除了,而是做运算!
|
||||||
|
|
||||||
C++代码如下:
|
C++代码如下:
|
||||||
|
|
||||||
@ -126,7 +125,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 其他语言版本
|
# 其他语言版本
|
||||||
|
|
||||||
java:
|
java:
|
||||||
|
|
||||||
@ -153,23 +152,20 @@ public class EvalRPN {
|
|||||||
}
|
}
|
||||||
return stack.pop();
|
return stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isOpe(String s) {
|
private boolean isOpe(String s) {
|
||||||
return s.length() == 1 && s.charAt(0) <'0' || s.charAt(0) >'9';
|
return s.length() == 1 && s.charAt(0) <'0' || s.charAt(0) >'9';
|
||||||
}
|
}
|
||||||
|
|
||||||
private int stoi(String s) {
|
private int stoi(String s) {
|
||||||
return Integer.valueOf(s);
|
return Integer.valueOf(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new EvalRPN().evalRPN(new String[] {"10","6","9","3","+","-11","*","/","*","17","+","5","+"});
|
new EvalRPN().evalRPN(new String[] {"10","6","9","3","+","-11","*","/","*","17","+","5","+"});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
```Go
|
```Go
|
||||||
func evalRPN(tokens []string) int {
|
func evalRPN(tokens []string) int {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
|
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
|
||||||
|
|
||||||
相对于[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),无非就是多了一个限制,本题是要找到和为n的k个数的组合,而整个集合已经是固定的了[1,...,9]。
|
相对于[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),无非就是多了一个限制,本题是要找到和为n的k个数的组合,而整个集合已经是固定的了[1,...,9]。
|
||||||
|
|
||||||
想到这一点了,做过[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)之后,本题是简单一些了。
|
想到这一点了,做过[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)之后,本题是简单一些了。
|
||||||
|
|
||||||
@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
* **确定递归函数参数**
|
* **确定递归函数参数**
|
||||||
|
|
||||||
和[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。
|
和[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。
|
||||||
|
|
||||||
这里我依然定义path 和 result为全局变量。
|
这里我依然定义path 和 result为全局变量。
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ if (path.size() == k) {
|
|||||||
|
|
||||||
* **单层搜索过程**
|
* **单层搜索过程**
|
||||||
|
|
||||||
本题和[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)区别之一就是集合固定的就是9个数[1,...,9],所以for循环固定i<=9
|
本题和[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)区别之一就是集合固定的就是9个数[1,...,9],所以for循环固定i<=9
|
||||||
|
|
||||||
如图:
|
如图:
|
||||||

|

|
||||||
@ -227,6 +227,44 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
Java:
|
Java:
|
||||||
|
|
||||||
|
模板方法
|
||||||
|
```java
|
||||||
|
class Solution {
|
||||||
|
List<List<Integer>> result = new ArrayList<>();
|
||||||
|
LinkedList<Integer> path = new LinkedList<>();
|
||||||
|
|
||||||
|
public List<List<Integer>> combinationSum3(int k, int n) {
|
||||||
|
backTracking(n, k, 1, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backTracking(int targetSum, int k, int startIndex, int sum) {
|
||||||
|
// 减枝
|
||||||
|
if (sum > targetSum) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.size() == k) {
|
||||||
|
if (sum == targetSum) result.add(new ArrayList<>(path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 减枝 9 - (k - path.size()) + 1
|
||||||
|
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
|
||||||
|
path.add(i);
|
||||||
|
sum += i;
|
||||||
|
backTracking(targetSum, k, i + 1, sum);
|
||||||
|
//回溯
|
||||||
|
path.removeLast();
|
||||||
|
//回溯
|
||||||
|
sum -= i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其他方法
|
||||||
```java
|
```java
|
||||||
class Solution {
|
class Solution {
|
||||||
List<List<Integer>> res = new ArrayList<>();
|
List<List<Integer>> res = new ArrayList<>();
|
||||||
|
@ -22,13 +22,13 @@ https://leetcode-cn.com/problems/sliding-window-maximum/
|
|||||||
|
|
||||||
你能在线性时间复杂度内解决此题吗?
|
你能在线性时间复杂度内解决此题吗?
|
||||||
|
|
||||||
<img src='https://code-thinking.cdn.bcebos.com/pics/239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.png' width=600> </img></div>
|
<img src='https://code-thinking.cdn.bcebos.com/pics/239.滑动窗口最大值.png' width=600> </img></div>
|
||||||
|
|
||||||
提示:
|
提示:
|
||||||
|
|
||||||
1 <= nums.length <= 10^5
|
* 1 <= nums.length <= 10^5
|
||||||
-10^4 <= nums[i] <= 10^4
|
* -10^4 <= nums[i] <= 10^4
|
||||||
1 <= k <= nums.length
|
* 1 <= k <= nums.length
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ public:
|
|||||||
|
|
||||||
动画如下:
|
动画如下:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
对于窗口里的元素{2, 3, 5, 1 ,4},单调队列里只维护{5, 4} 就够了,保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。
|
对于窗口里的元素{2, 3, 5, 1 ,4},单调队列里只维护{5, 4} 就够了,保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。
|
||||||
|
|
||||||
@ -100,11 +100,11 @@ public:
|
|||||||
为了更直观的感受到单调队列的工作过程,以题目示例为例,输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3,动画如下:
|
为了更直观的感受到单调队列的工作过程,以题目示例为例,输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3,动画如下:
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
那么我们用什么数据结构来实现这个单调队列呢?
|
那么我们用什么数据结构来实现这个单调队列呢?
|
||||||
|
|
||||||
使用deque最为合适,在文章[栈与队列:来看看栈和队列不为人知的一面](https://mp.weixin.qq.com/s/VZRjOccyE09aE-MgLbCMjQ)中,我们就提到了常用的queue在没有指定容器的情况下,deque就是默认底层容器。
|
使用deque最为合适,在文章[栈与队列:来看看栈和队列不为人知的一面](https://mp.weixin.qq.com/s/HCXfQ_Bhpi63YaX0ZRSnAQ)中,我们就提到了常用的queue在没有指定容器的情况下,deque就是默认底层容器。
|
||||||
|
|
||||||
基于刚刚说过的单调队列pop和push的规则,代码不难实现,如下:
|
基于刚刚说过的单调队列pop和push的规则,代码不难实现,如下:
|
||||||
|
|
||||||
@ -201,9 +201,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 其他语言版本
|
||||||
|
|
||||||
## 其他语言版本
|
|
||||||
|
|
||||||
|
|
||||||
Java:
|
Java:
|
||||||
@ -337,36 +335,6 @@ class Solution:
|
|||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
|
||||||
```go
|
|
||||||
func maxSlidingWindow(nums []int, k int) []int {
|
|
||||||
var queue []int
|
|
||||||
var rtn []int
|
|
||||||
|
|
||||||
for f := 0; f < len(nums); f++ {
|
|
||||||
//维持队列递减, 将 k 插入合适的位置, queue中 <=k 的 元素都不可能是窗口中的最大值, 直接弹出
|
|
||||||
for len(queue) > 0 && nums[f] > nums[queue[len(queue)-1]] {
|
|
||||||
queue = queue[:len(queue)-1]
|
|
||||||
}
|
|
||||||
// 等大的后来者也应入队
|
|
||||||
if len(queue) == 0 || nums[f] <= nums[queue[len(queue)-1]] {
|
|
||||||
queue = append(queue, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f >= k - 1 {
|
|
||||||
rtn = append(rtn, nums[queue[0]])
|
|
||||||
//弹出离开窗口的队首
|
|
||||||
if f - k + 1 == queue[0] {
|
|
||||||
queue = queue[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtn
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// 封装单调队列的方式解题
|
// 封装单调队列的方式解题
|
||||||
type MyQueue struct {
|
type MyQueue struct {
|
||||||
|
@ -304,6 +304,26 @@ func min(a, b int) int {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Javascript:
|
||||||
|
```javascript
|
||||||
|
const coinChange = (coins, amount) => {
|
||||||
|
if(!amount) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dp = Array(amount + 1).fill(Infinity);
|
||||||
|
dp[0] = 0;
|
||||||
|
|
||||||
|
for(let i =0; i < coins.length; i++) {
|
||||||
|
for(let j = coins[i]; j <= amount; j++) {
|
||||||
|
dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[amount] === Infinity ? -1 : dp[amount];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -33,6 +33,9 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
这道题目还是很难的,之前我们用回溯法解决了如下问题:[组合问题](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),[分割问题](https://mp.weixin.qq.com/s/v--VmA8tp9vs4bXCqHhBuA),[子集问题](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA),[排列问题](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw)。
|
这道题目还是很难的,之前我们用回溯法解决了如下问题:[组合问题](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),[分割问题](https://mp.weixin.qq.com/s/v--VmA8tp9vs4bXCqHhBuA),[子集问题](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA),[排列问题](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw)。
|
||||||
|
|
||||||
直觉上来看 这道题和回溯法没有什么关系,更像是图论中的深度优先搜索。
|
直觉上来看 这道题和回溯法没有什么关系,更像是图论中的深度优先搜索。
|
||||||
|
@ -201,6 +201,25 @@ func combinationSum4(nums []int, target int) int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Javascript:
|
||||||
|
```javascript
|
||||||
|
const combinationSum4 = (nums, target) => {
|
||||||
|
|
||||||
|
let dp = Array(target + 1).fill(0);
|
||||||
|
dp[0] = 1;
|
||||||
|
|
||||||
|
for(let i = 0; i <= target; i++) {
|
||||||
|
for(let j = 0; j < nums.length; j++) {
|
||||||
|
if (i >= nums[j]) {
|
||||||
|
dp[i] += dp[i - nums[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[target];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -43,9 +43,9 @@
|
|||||||
|
|
||||||
本题有两个维度,h和k,看到这种题目一定要想如何确定一个维度,然后在按照另一个维度重新排列。
|
本题有两个维度,h和k,看到这种题目一定要想如何确定一个维度,然后在按照另一个维度重新排列。
|
||||||
|
|
||||||
其实如果大家认真做了[贪心算法:分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ),就会发现和此题有点点的像。
|
其实如果大家认真做了[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ),就会发现和此题有点点的像。
|
||||||
|
|
||||||
在[贪心算法:分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)我就强调过一次,遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。
|
在[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)我就强调过一次,遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。
|
||||||
|
|
||||||
**如果两个维度一起考虑一定会顾此失彼**。
|
**如果两个维度一起考虑一定会顾此失彼**。
|
||||||
|
|
||||||
@ -161,11 +161,11 @@ public:
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
关于出现两个维度一起考虑的情况,我们已经做过两道题目了,另一道就是[贪心算法:分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)。
|
关于出现两个维度一起考虑的情况,我们已经做过两道题目了,另一道就是[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)。
|
||||||
|
|
||||||
**其技巧都是确定一边然后贪心另一边,两边一起考虑,就会顾此失彼**。
|
**其技巧都是确定一边然后贪心另一边,两边一起考虑,就会顾此失彼**。
|
||||||
|
|
||||||
这道题目可以说比[贪心算法:分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)难不少,其贪心的策略也是比较巧妙。
|
这道题目可以说比[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)难不少,其贪心的策略也是比较巧妙。
|
||||||
|
|
||||||
最后我给出了两个版本的代码,可以明显看是使用C++中的list(底层链表实现)比vector(数组)效率高得多。
|
最后我给出了两个版本的代码,可以明显看是使用C++中的list(底层链表实现)比vector(数组)效率高得多。
|
||||||
|
|
||||||
|
@ -122,9 +122,9 @@ public:
|
|||||||
|
|
||||||
## 补充
|
## 补充
|
||||||
|
|
||||||
本题其实和[贪心算法:用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
|
本题其实和[452.用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
|
||||||
|
|
||||||
把[贪心算法:用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)代码稍做修改,就可以AC本题。
|
把[452.用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)代码稍做修改,就可以AC本题。
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
class Solution {
|
class Solution {
|
||||||
|
@ -244,6 +244,35 @@ func max(a,b int) int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Javascript:
|
||||||
|
```javascript
|
||||||
|
const findMaxForm = (strs, m, n) => {
|
||||||
|
const dp = Array.from(Array(m+1), () => Array(n+1).fill(0));
|
||||||
|
let numOfZeros, numOfOnes;
|
||||||
|
|
||||||
|
for(let str of strs) {
|
||||||
|
numOfZeros = 0;
|
||||||
|
numOfOnes = 0;
|
||||||
|
|
||||||
|
for(let c of str) {
|
||||||
|
if (c === '0') {
|
||||||
|
numOfZeros++;
|
||||||
|
} else {
|
||||||
|
numOfOnes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let i = m; i >= numOfZeros; i--) {
|
||||||
|
for(let j = n; j >= numOfOnes; j--) {
|
||||||
|
dp[i][j] = Math.max(dp[i][j], dp[i - numOfZeros][j - numOfOnes] + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[m][n];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。
|
这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。
|
||||||
|
|
||||||
这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)。
|
这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)。
|
||||||
@ -254,6 +257,34 @@ class Solution:
|
|||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
func findSubsequences(nums []int) [][]int {
|
||||||
|
var subRes []int
|
||||||
|
var res [][]int
|
||||||
|
backTring(0,nums,subRes,&res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
func backTring(startIndex int,nums,subRes []int,res *[][]int){
|
||||||
|
if len(subRes)>1{
|
||||||
|
tmp:=make([]int,len(subRes))
|
||||||
|
copy(tmp,subRes)
|
||||||
|
*res=append(*res,tmp)
|
||||||
|
}
|
||||||
|
history:=[201]int{}//记录本层元素使用记录
|
||||||
|
for i:=startIndex;i<len(nums);i++{
|
||||||
|
//分两种情况判断:一,当前取的元素小于子集的最后一个元素,则继续寻找下一个适合的元素
|
||||||
|
// 或者二,当前取的元素在本层已经出现过了,所以跳过该元素,继续寻找
|
||||||
|
if len(subRes)>0&&nums[i]<subRes[len(subRes)-1]||history[nums[i] + 100]==1{
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
history[nums[i] + 100]=1//表示本层该元素使用过了
|
||||||
|
subRes=append(subRes,nums[i])
|
||||||
|
backTring(i+1,nums,subRes,res)
|
||||||
|
subRes=subRes[:len(subRes)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Javascript:
|
Javascript:
|
||||||
|
|
||||||
```Javascript
|
```Javascript
|
||||||
|
@ -306,6 +306,36 @@ func findTargetSumWays(nums []int, target int) int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Javascript:
|
||||||
|
```javascript
|
||||||
|
const findTargetSumWays = (nums, target) => {
|
||||||
|
|
||||||
|
const sum = nums.reduce((a, b) => a+b);
|
||||||
|
|
||||||
|
if(target > sum) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((target + sum) % 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const halfSum = (target + sum) / 2;
|
||||||
|
nums.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
let dp = new Array(halfSum+1).fill(0);
|
||||||
|
dp[0] = 1;
|
||||||
|
|
||||||
|
for(let i = 0; i < nums.length; i++) {
|
||||||
|
for(let j = halfSum; j >= nums[i]; j--) {
|
||||||
|
dp[j] += dp[j - nums[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[halfSum];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
210
problems/0496.下一个更大元素I.md
Normal file
210
problems/0496.下一个更大元素I.md
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
|
||||||
|
# 496.下一个更大元素 I
|
||||||
|
|
||||||
|
题目链接:https://leetcode-cn.com/problems/next-greater-element-i/
|
||||||
|
|
||||||
|
给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。
|
||||||
|
|
||||||
|
请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。
|
||||||
|
|
||||||
|
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
|
||||||
|
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
|
||||||
|
输出: [-1,3,-1]
|
||||||
|
解释:
|
||||||
|
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
|
||||||
|
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
|
||||||
|
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入: nums1 = [2,4], nums2 = [1,2,3,4].
|
||||||
|
输出: [3,-1]
|
||||||
|
解释:
|
||||||
|
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
|
||||||
|
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出-1 。
|
||||||
|
|
||||||
|
提示:
|
||||||
|
|
||||||
|
* 1 <= nums1.length <= nums2.length <= 1000
|
||||||
|
* 0 <= nums1[i], nums2[i] <= 10^4
|
||||||
|
* nums1和nums2中所有整数 互不相同
|
||||||
|
* nums1 中的所有整数同样出现在 nums2 中
|
||||||
|
|
||||||
|
# 思路
|
||||||
|
|
||||||
|
做本题之前,建议先做一下[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q)
|
||||||
|
|
||||||
|
在[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q)中是求每个元素下一个比当前元素大的元素的位置。
|
||||||
|
|
||||||
|
本题则是说nums1 是 nums2的子集,找nums1中的元素在nums2中下一个比当前元素大的元素。
|
||||||
|
|
||||||
|
看上去和[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q) 就如出一辙了。
|
||||||
|
|
||||||
|
几乎是一样的,但是这么绕了一下,其实还上升了一点难度。
|
||||||
|
|
||||||
|
需要对单调栈使用的更熟练一些,才能顺利的把本题写出来。
|
||||||
|
|
||||||
|
从题目示例中我们可以看出最后是要求nums1的每个元素在nums2中下一个比当前元素大的元素,那么就要定义一个和nums1一样大小的数组result来存放结果。
|
||||||
|
|
||||||
|
一些同学可能看到两个数组都已经懵了,不知道要定一个一个多大的result数组来存放结果了。
|
||||||
|
|
||||||
|
**这么定义这个result数组初始化应该为多少呢?**
|
||||||
|
|
||||||
|
题目说如果不存在对应位置就输出 -1 ,所以result数组如果某位置没有被赋值,那么就应该是是-1,所以就初始化为-1。
|
||||||
|
|
||||||
|
在遍历nums2的过程中,我们要判断nums2[i]是否在nums1中出现过,因为最后是要根据nums1元素的下标来更新result数组。
|
||||||
|
|
||||||
|
**注意题目中说是两个没有重复元素 的数组 nums1 和 nums2**。
|
||||||
|
|
||||||
|
没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
|
||||||
|
|
||||||
|
C++中,当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的。我在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中也做了详细的解释。
|
||||||
|
|
||||||
|
那么预处理代码如下:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
unordered_map<int, int> umap; // key:下表元素,value:下表
|
||||||
|
for (int i = 0; i < nums1.size(); i++) {
|
||||||
|
umap[nums1[i]] = i;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用单调栈,首先要想单调栈是从大到小还是从小到大。
|
||||||
|
|
||||||
|
本题和739. 每日温度是一样的。
|
||||||
|
|
||||||
|
栈头到栈底的顺序,要从小到大,也就是保持栈里的元素为递增顺序。只要保持递增,才能找到右边第一个比自己大的元素。
|
||||||
|
|
||||||
|
可能这里有一些同学不理解,那么可以自己尝试一下用递减栈,能不能求出来。其实递减栈就是求右边第一个比自己小的元素了。
|
||||||
|
|
||||||
|
|
||||||
|
接下来就要分析如下三种情况,一定要分析清楚。
|
||||||
|
|
||||||
|
1. 情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
|
||||||
|
|
||||||
|
此时满足递增栈(栈头到栈底的顺序),所以直接入栈。
|
||||||
|
|
||||||
|
2. 情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
|
||||||
|
|
||||||
|
如果相等的话,依然直接入栈,因为我们要求的是右边第一个比自己大的元素,而不是大于等于!
|
||||||
|
|
||||||
|
3. 情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
|
||||||
|
|
||||||
|
此时如果入栈就不满足递增栈了,这也是找到右边第一个比自己大的元素的时候。
|
||||||
|
|
||||||
|
判断栈顶元素是否在nums1里出现过,(注意栈里的元素是nums2的元素),如果出现过,开始记录结果。
|
||||||
|
|
||||||
|
记录结果这块逻辑有一点小绕,要清楚,此时栈顶元素在nums2中右面第一个大的元素是nums2[i]即当前遍历元素。
|
||||||
|
|
||||||
|
代码如下:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
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);
|
||||||
|
```
|
||||||
|
|
||||||
|
以上分析完毕,C++代码如下:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
// 版本一
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
针对版本一,进行代码精简后,代码如下:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
// 版本二
|
||||||
|
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++) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
精简的代码是直接把情况一二三都合并到了一起,其实这种代码精简是精简,但思路不是很清晰。
|
||||||
|
|
||||||
|
建议大家把情况一二三想清楚了,先写出版本一的代码,然后在其基础上在做精简!
|
||||||
|
|
||||||
|
## 其他语言版本
|
||||||
|
|
||||||
|
Python:
|
||||||
|
```python3
|
||||||
|
class Solution:
|
||||||
|
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
|
||||||
|
result = [-1]*len(nums1)
|
||||||
|
stack = [0]
|
||||||
|
for i in range(1,len(nums2)):
|
||||||
|
# 情况一情况二
|
||||||
|
if nums2[i]<=nums2[stack[-1]]:
|
||||||
|
stack.append(i)
|
||||||
|
# 情况三
|
||||||
|
else:
|
||||||
|
while len(stack)!=0 and nums2[i]>nums2[stack[-1]]:
|
||||||
|
if nums2[stack[-1]] in nums1:
|
||||||
|
index = nums1.index(nums2[stack[-1]])
|
||||||
|
result[index]=nums2[i]
|
||||||
|
stack.pop()
|
||||||
|
stack.append(i)
|
||||||
|
return result
|
||||||
|
```
|
||||||
|
|
@ -243,6 +243,22 @@ func change(amount int, coins []int) int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Javascript:
|
||||||
|
```javascript
|
||||||
|
const change = (amount, coins) => {
|
||||||
|
let dp = Array(amount + 1).fill(0);
|
||||||
|
dp[0] = 1;
|
||||||
|
|
||||||
|
for(let i =0; i < coins.length; i++) {
|
||||||
|
for(let j = coins[i]; j <= amount; j++) {
|
||||||
|
dp[j] += dp[j - coins[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[amount];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -319,7 +319,7 @@ Python:
|
|||||||
# self.val = val
|
# self.val = val
|
||||||
# self.left = left
|
# self.left = left
|
||||||
# self.right = right
|
# self.right = right
|
||||||
//递归法*前序遍历
|
# 递归法*前序遍历
|
||||||
class Solution:
|
class Solution:
|
||||||
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
|
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
|
||||||
if not root1: return root2 // 如果t1为空,合并之后就应该是t2
|
if not root1: return root2 // 如果t1为空,合并之后就应该是t2
|
||||||
@ -328,6 +328,32 @@ class Solution:
|
|||||||
root1.left = self.mergeTrees(root1.left , root2.left) //左
|
root1.left = self.mergeTrees(root1.left , root2.left) //左
|
||||||
root1.right = self.mergeTrees(root1.right , root2.right) //右
|
root1.right = self.mergeTrees(root1.right , root2.right) //右
|
||||||
return root1 //root1修改了结构和数值
|
return root1 //root1修改了结构和数值
|
||||||
|
|
||||||
|
# 迭代法-覆盖原来的树
|
||||||
|
class Solution:
|
||||||
|
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
|
||||||
|
if not root1: return root2
|
||||||
|
if not root2: return root1
|
||||||
|
# 迭代,将树2覆盖到树1
|
||||||
|
queue1 = [root1]
|
||||||
|
queue2 = [root2]
|
||||||
|
root = root1
|
||||||
|
while queue1 and queue2:
|
||||||
|
root1 = queue1.pop(0)
|
||||||
|
root2 = queue2.pop(0)
|
||||||
|
root1.val += root2.val
|
||||||
|
if not root1.left: # 如果树1左儿子不存在,则覆盖后树1的左儿子为树2的左儿子
|
||||||
|
root1.left = root2.left
|
||||||
|
elif root1.left and root2.left:
|
||||||
|
queue1.append(root1.left)
|
||||||
|
queue2.append(root2.left)
|
||||||
|
|
||||||
|
if not root1.right: # 同理,处理右儿子
|
||||||
|
root1.right = root2.right
|
||||||
|
elif root1.right and root2.right:
|
||||||
|
queue1.append(root1.right)
|
||||||
|
queue2.append(root2.right)
|
||||||
|
return root
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
@ -211,7 +211,24 @@ Java:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
Python:
|
Python:
|
||||||
|
``` Python3
|
||||||
|
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:
|
||||||
|
|
||||||
> 暴力法
|
> 暴力法
|
||||||
|
@ -99,10 +99,17 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
|
|||||||
|
|
||||||
状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
|
状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
|
||||||
|
|
||||||
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
|
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
|
||||||
|
|
||||||
代码如下:
|
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
|
||||||
```
|
|
||||||
|
当j >= weight[0]是,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||||
|
|
||||||
|
代码初始化如下:
|
||||||
|
```
|
||||||
|
for (int j = 0 ; j < weight[0]; j++) { // 当然这一步,如果把dp数组预先初始化为0了,这一步就可以省略,但很多同学应该没有想清楚这一点。
|
||||||
|
dp[0][j] = 0;
|
||||||
|
}
|
||||||
// 正序遍历
|
// 正序遍历
|
||||||
for (int j = weight[0]; j <= bagWeight; j++) {
|
for (int j = weight[0]; j <= bagWeight; j++) {
|
||||||
dp[0][j] = value[0];
|
dp[0][j] = value[0];
|
||||||
@ -116,14 +123,11 @@ for (int j = weight[0]; j <= bagWeight; j++) {
|
|||||||
|
|
||||||
dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?
|
dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?
|
||||||
|
|
||||||
|
其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是又左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。
|
||||||
|
|
||||||
dp[i][j]在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,因为0就是最小的了,不会影响取最大价值的结果。
|
初始-1,初始-2,初始100,都可以!
|
||||||
|
|
||||||
如果题目给的价值有负数,那么非0下标就要初始化为负无穷了。例如:一个物品的价值是-2,但对应的位置依然初始化为0,那么取最大值的时候,就会取0而不是-2了,所以要初始化为负无穷。
|
但只不过一开始就统一把dp数组统一初始为0,更方便一些。
|
||||||
|
|
||||||
而背包问题的物品价值都是正整数,所以初始化为0,就可以了。
|
|
||||||
|
|
||||||
**这样才能让dp数组在递归公式的过程中取最大的价值,而不是被初始值覆盖了**。
|
|
||||||
|
|
||||||
如图:
|
如图:
|
||||||
|
|
||||||
@ -159,7 +163,7 @@ for (int j = weight[0]; j <= bagWeight; j++) {
|
|||||||
// weight数组的大小 就是物品个数
|
// weight数组的大小 就是物品个数
|
||||||
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
||||||
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
|
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
|
||||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 这个是为了展现dp数组里元素的变化
|
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||||
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -176,9 +176,48 @@ int main() {
|
|||||||
|
|
||||||
## 其他语言版本
|
## 其他语言版本
|
||||||
|
|
||||||
|
|
||||||
Java:
|
Java:
|
||||||
|
|
||||||
|
```java
|
||||||
|
//先遍历物品,再遍历背包
|
||||||
|
private static void testCompletePack(){
|
||||||
|
int[] weight = {1, 3, 4};
|
||||||
|
int[] value = {15, 20, 30};
|
||||||
|
int bagWeight = 4;
|
||||||
|
int[] dp = new int[bagWeight + 1];
|
||||||
|
for (int i = 0; i < weight.length; i++){
|
||||||
|
for (int j = 1; j <= bagWeight; j++){
|
||||||
|
if (j - weight[i] >= 0){
|
||||||
|
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int maxValue : dp){
|
||||||
|
System.out.println(maxValue + " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//先遍历背包,再遍历物品
|
||||||
|
private static void testCompletePackAnotherWay(){
|
||||||
|
int[] weight = {1, 3, 4};
|
||||||
|
int[] value = {15, 20, 30};
|
||||||
|
int bagWeight = 4;
|
||||||
|
int[] dp = new int[bagWeight + 1];
|
||||||
|
for (int i = 1; i <= bagWeight; i++){
|
||||||
|
for (int j = 0; j < weight.length; j++){
|
||||||
|
if (i - weight[j] >= 0){
|
||||||
|
dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int maxValue : dp){
|
||||||
|
System.out.println(maxValue + " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
```python3
|
```python3
|
||||||
|
Reference in New Issue
Block a user