mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-09 11:34:46 +08:00
Merge branch 'youngyangyang04:master' into master
This commit is contained in:
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# 5.最长回文子串
|
# 5.最长回文子串
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/
|
[力扣题目链接](https://leetcode-cn.com/problems/longest-palindromic-substring/)
|
||||||
|
|
||||||
给你一个字符串 s,找到 s 中最长的回文子串。
|
给你一个字符串 s,找到 s 中最长的回文子串。
|
||||||
|
|
||||||
@ -30,11 +30,11 @@
|
|||||||
示例 4:
|
示例 4:
|
||||||
* 输入:s = "ac"
|
* 输入:s = "ac"
|
||||||
* 输出:"a"
|
* 输出:"a"
|
||||||
|
|
||||||
|
|
||||||
# 思路
|
# 思路
|
||||||
|
|
||||||
本题和[647.回文子串](https://mp.weixin.qq.com/s/2WetyP6IYQ6VotegepVpEw) 差不多是一样的,但647.回文子串更基本一点,建议可以先做647.回文子串
|
本题和[647.回文子串](https://programmercarl.com/0647.回文子串.html) 差不多是一样的,但647.回文子串更基本一点,建议可以先做647.回文子串
|
||||||
|
|
||||||
## 暴力解法
|
## 暴力解法
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
> 用哈希表解决了[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ),那么三数之和呢?
|
> 用哈希表解决了[两数之和](https://programmercarl.com/0001.两数之和.html),那么三数之和呢?
|
||||||
|
|
||||||
# 第15题. 三数之和
|
# 第15题. 三数之和
|
||||||
|
|
||||||
https://leetcode-cn.com/problems/3sum/
|
[力扣题目链接](https://leetcode-cn.com/problems/3sum/)
|
||||||
|
|
||||||
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
|
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ https://leetcode-cn.com/problems/3sum/
|
|||||||
|
|
||||||
两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。
|
两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。
|
||||||
|
|
||||||
把符合条件的三元组放进vector中,然后在去去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。
|
把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。
|
||||||
|
|
||||||
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
|
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
|
||||||
|
|
||||||
@ -95,11 +95,11 @@ public:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下表0的地方开始,同时定一个下表left 定义在i+1的位置上,定义下表right 在数组结尾的位置上。
|
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
|
||||||
|
|
||||||
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i] b = nums[left] c = nums[right]。
|
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i] b = nums[left] c = nums[right]。
|
||||||
|
|
||||||
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下表就应该向左移动,这样才能让三数之和小一些。
|
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
|
||||||
|
|
||||||
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
|
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
|
||||||
|
|
||||||
@ -163,13 +163,13 @@ public:
|
|||||||
# 思考题
|
# 思考题
|
||||||
|
|
||||||
|
|
||||||
既然三数之和可以使用双指针法,我们之前讲过的[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ),可不可以使用双指针法呢?
|
既然三数之和可以使用双指针法,我们之前讲过的[1.两数之和](https://programmercarl.com/0001.两数之和.html),可不可以使用双指针法呢?
|
||||||
|
|
||||||
如果不能,题意如何更改就可以使用双指针法呢? **大家留言说出自己的想法吧!**
|
如果不能,题意如何更改就可以使用双指针法呢? **大家留言说出自己的想法吧!**
|
||||||
|
|
||||||
两数之和 就不能使用双指针法,因为[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)要求返回的是索引下表, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
|
两数之和 就不能使用双指针法,因为[1.两数之和](https://programmercarl.com/0001.两数之和.html)要求返回的是索引下标, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
|
||||||
|
|
||||||
如果[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)要求返回的是数值的话,就可以使用双指针法了。
|
如果[1.两数之和](https://programmercarl.com/0001.两数之和.html)要求返回的是数值的话,就可以使用双指针法了。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
# 17.电话号码的字母组合
|
# 17.电话号码的字母组合
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
|
[力扣题目链接](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/)
|
||||||
|
|
||||||
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
|
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
|
||||||
|
|
||||||
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
如果输入"233"呢,那么就三层for循环,如果"2333"呢,就四层for循环.......
|
如果输入"233"呢,那么就三层for循环,如果"2333"呢,就四层for循环.......
|
||||||
|
|
||||||
大家应该感觉出和[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)遇到的一样的问题,就是这for循环的层数如何写出来,此时又是回溯法登场的时候了。
|
大家应该感觉出和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)遇到的一样的问题,就是这for循环的层数如何写出来,此时又是回溯法登场的时候了。
|
||||||
|
|
||||||
理解本题后,要解决如下三个问题:
|
理解本题后,要解决如下三个问题:
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ const string letterMap[10] = {
|
|||||||
|
|
||||||
## 回溯法来解决n个for循环的问题
|
## 回溯法来解决n个for循环的问题
|
||||||
|
|
||||||
对于回溯法还不了解的同学看这篇:[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)
|
对于回溯法还不了解的同学看这篇:[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)
|
||||||
|
|
||||||
|
|
||||||
例如:输入:"23",抽象为树形结构,如图所示:
|
例如:输入:"23",抽象为树形结构,如图所示:
|
||||||
@ -75,7 +75,7 @@ const string letterMap[10] = {
|
|||||||
|
|
||||||
再来看参数,参数指定是有题目中给的string digits,然后还要有一个参数就是int型的index。
|
再来看参数,参数指定是有题目中给的string digits,然后还要有一个参数就是int型的index。
|
||||||
|
|
||||||
注意这个index可不是 [回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)和[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)中的startIndex了。
|
注意这个index可不是 [回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)中的startIndex了。
|
||||||
|
|
||||||
这个index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。
|
这个index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。
|
||||||
|
|
||||||
@ -120,9 +120,9 @@ for (int i = 0; i < letters.size(); i++) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**注意这里for循环,可不像是在[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)和[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)中从startIndex开始遍历的**。
|
**注意这里for循环,可不像是在[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)中从startIndex开始遍历的**。
|
||||||
|
|
||||||
**因为本题每一个数字代表的是不同集合,也就是求不同集合之间的组合,而[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)和[216.组合总和III](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)都是是求同一个集合中的组合!**
|
**因为本题每一个数字代表的是不同集合,也就是求不同集合之间的组合,而[77. 组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)都是是求同一个集合中的组合!**
|
||||||
|
|
||||||
|
|
||||||
注意:输入1 * #按键等等异常情况
|
注意:输入1 * #按键等等异常情况
|
||||||
@ -134,7 +134,7 @@ for (int i = 0; i < letters.size(); i++) {
|
|||||||
|
|
||||||
## C++代码
|
## C++代码
|
||||||
|
|
||||||
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中的回溯法模板,不难写出如下C++代码:
|
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中的回溯法模板,不难写出如下C++代码:
|
||||||
|
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
@ -224,13 +224,13 @@ public:
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
我不建议把回溯藏在递归的参数里这种写法,很不直观,我在[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA)这篇文章中也深度分析了,回溯隐藏在了哪里。
|
我不建议把回溯藏在递归的参数里这种写法,很不直观,我在[二叉树:以为使用了递归,其实还隐藏着回溯](https://programmercarl.com/二叉树中递归带着回溯.html)这篇文章中也深度分析了,回溯隐藏在了哪里。
|
||||||
|
|
||||||
所以大家可以按照版本一来写就可以了。
|
所以大家可以按照版本一来写就可以了。
|
||||||
|
|
||||||
# 总结
|
# 总结
|
||||||
|
|
||||||
本篇将题目的三个要点一一列出,并重点强调了和前面讲解过的[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)和[216.组合总和III](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)的区别,本题是多个集合求组合,所以在回溯的搜索过程中,都有一些细节需要注意的。
|
本篇将题目的三个要点一一列出,并重点强调了和前面讲解过的[77. 组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)的区别,本题是多个集合求组合,所以在回溯的搜索过程中,都有一些细节需要注意的。
|
||||||
|
|
||||||
其实本题不算难,但也处处是细节,大家还要自己亲自动手写一写。
|
其实本题不算难,但也处处是细节,大家还要自己亲自动手写一写。
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
# 第18题. 四数之和
|
# 第18题. 四数之和
|
||||||
|
|
||||||
https://leetcode-cn.com/problems/4sum/
|
[力扣题目链接](https://leetcode-cn.com/problems/4sum/)
|
||||||
|
|
||||||
题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
|
题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
|
||||||
|
|
||||||
@ -31,37 +31,37 @@ https://leetcode-cn.com/problems/4sum/
|
|||||||
|
|
||||||
# 思路
|
# 思路
|
||||||
|
|
||||||
四数之和,和[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)是一个思路,都是使用双指针法, 基本解法就是在[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg) 的基础上再套一层for循环。
|
四数之和,和[15.三数之和](https://programmercarl.com/0015.三数之和.html)是一个思路,都是使用双指针法, 基本解法就是在[15.三数之和](https://programmercarl.com/0015.三数之和.html) 的基础上再套一层for循环。
|
||||||
|
|
||||||
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
|
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
|
||||||
|
|
||||||
[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
|
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
|
||||||
|
|
||||||
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下表作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
|
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下表作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
|
||||||
|
|
||||||
那么一样的道理,五数之和、六数之和等等都采用这种解法。
|
那么一样的道理,五数之和、六数之和等等都采用这种解法。
|
||||||
|
|
||||||
对于[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
|
对于[15.三数之和](https://programmercarl.com/0015.三数之和.html)双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
|
||||||
|
|
||||||
之前我们讲过哈希表的经典题目:[454.四数相加II](https://mp.weixin.qq.com/s/12g_w6RzHuEpFts1pT6BWw),相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。
|
之前我们讲过哈希表的经典题目:[454.四数相加II](https://programmercarl.com/0454.四数相加II.html),相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。
|
||||||
|
|
||||||
而[454.四数相加II](https://mp.weixin.qq.com/s/12g_w6RzHuEpFts1pT6BWw)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
|
而[454.四数相加II](https://programmercarl.com/0454.四数相加II.html)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
|
||||||
|
|
||||||
我们来回顾一下,几道题目使用了双指针法。
|
我们来回顾一下,几道题目使用了双指针法。
|
||||||
|
|
||||||
双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
|
双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
|
||||||
|
|
||||||
* [27.移除元素](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww)
|
* [27.移除元素](https://programmercarl.com/0027.移除元素.html)
|
||||||
* [15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)
|
* [15.三数之和](https://programmercarl.com/0015.三数之和.html)
|
||||||
* [18.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
|
* [18.四数之和](https://programmercarl.com/0018.四数之和.html)
|
||||||
|
|
||||||
|
|
||||||
操作链表:
|
操作链表:
|
||||||
|
|
||||||
* [206.反转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)
|
* [206.反转链表](https://programmercarl.com/0206.翻转链表.html)
|
||||||
* [19.删除链表的倒数第N个节点](https://mp.weixin.qq.com/s/gxu65X1343xW_sBrkTz0Eg)
|
* [19.删除链表的倒数第N个节点](https://programmercarl.com/0019.删除链表的倒数第N个节点.html)
|
||||||
* [面试题 02.07. 链表相交](https://mp.weixin.qq.com/s/BhfFfaGvt9Zs7UmH4YehZw)
|
* [面试题 02.07. 链表相交](https://programmercarl.com/面试题02.07.链表相交.html)
|
||||||
* [142题.环形链表II](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ)
|
* [142题.环形链表II](https://programmercarl.com/0142.环形链表II.html)
|
||||||
|
|
||||||
双指针法在字符串题目中还有很多应用,后面还会介绍到。
|
双指针法在字符串题目中还有很多应用,后面还会介绍到。
|
||||||
|
|
||||||
@ -167,7 +167,33 @@ class Solution {
|
|||||||
|
|
||||||
Python:
|
Python:
|
||||||
```python
|
```python
|
||||||
|
# 双指针法
|
||||||
|
class Solution:
|
||||||
|
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
|
||||||
|
|
||||||
|
nums.sort()
|
||||||
|
n = len(nums)
|
||||||
|
res = []
|
||||||
|
for i in range(n):
|
||||||
|
if i > 0 and nums[i] == nums[i - 1]: continue
|
||||||
|
for k in range(i+1, n):
|
||||||
|
if k > i + 1 and nums[k] == nums[k-1]: continue
|
||||||
|
p = k + 1
|
||||||
|
q = n - 1
|
||||||
|
|
||||||
|
while p < q:
|
||||||
|
if nums[i] + nums[k] + nums[p] + nums[q] > target: q -= 1
|
||||||
|
elif nums[i] + nums[k] + nums[p] + nums[q] < target: p += 1
|
||||||
|
else:
|
||||||
|
res.append([nums[i], nums[k], nums[p], nums[q]])
|
||||||
|
while p < q and nums[p] == nums[p + 1]: p += 1
|
||||||
|
while p < q and nums[q] == nums[q - 1]: q -= 1
|
||||||
|
p += 1
|
||||||
|
q -= 1
|
||||||
|
return res
|
||||||
|
```
|
||||||
|
```python
|
||||||
|
# 哈希表法
|
||||||
class Solution(object):
|
class Solution(object):
|
||||||
def fourSum(self, nums, target):
|
def fourSum(self, nums, target):
|
||||||
"""
|
"""
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## 19.删除链表的倒数第N个节点
|
## 19.删除链表的倒数第N个节点
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
|
[力扣题目链接](https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/)
|
||||||
|
|
||||||
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
|
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
|
||||||
|
|
||||||
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
分为如下几步:
|
分为如下几步:
|
||||||
|
|
||||||
* 首先这里我推荐大家使用虚拟头结点,这样方面处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://mp.weixin.qq.com/s/L5aanfALdLEwVWGvyXPDqA)
|
* 首先这里我推荐大家使用虚拟头结点,这样方面处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)
|
||||||
|
|
||||||
* 定义fast指针和slow指针,初始值为虚拟头结点,如图:
|
* 定义fast指针和slow指针,初始值为虚拟头结点,如图:
|
||||||
|
|
||||||
@ -204,6 +204,31 @@ fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Swift:
|
||||||
|
```swift
|
||||||
|
func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
|
||||||
|
if head == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return head
|
||||||
|
}
|
||||||
|
let dummyHead = ListNode(-1, head)
|
||||||
|
var fast: ListNode? = dummyHead
|
||||||
|
var slow: ListNode? = dummyHead
|
||||||
|
// fast 前移 n
|
||||||
|
for _ in 0 ..< n {
|
||||||
|
fast = fast?.next
|
||||||
|
}
|
||||||
|
while fast?.next != nil {
|
||||||
|
fast = fast?.next
|
||||||
|
slow = slow?.next
|
||||||
|
}
|
||||||
|
slow?.next = slow?.next?.next
|
||||||
|
return dummyHead.next
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
# 20. 有效的括号
|
# 20. 有效的括号
|
||||||
|
|
||||||
https://leetcode-cn.com/problems/valid-parentheses/
|
[力扣题目链接](https://leetcode-cn.com/problems/valid-parentheses/)
|
||||||
|
|
||||||
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
|
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
|
||||||
|
|
||||||
@ -162,18 +162,44 @@ class Solution {
|
|||||||
|
|
||||||
Python:
|
Python:
|
||||||
```python3
|
```python3
|
||||||
|
# 方法一,仅使用栈,更省空间
|
||||||
class Solution:
|
class Solution:
|
||||||
def isValid(self, s: str) -> bool:
|
def isValid(self, s: str) -> bool:
|
||||||
stack = [] # 保存还未匹配的左括号
|
stack = []
|
||||||
mapping = {")": "(", "]": "[", "}": "{"}
|
|
||||||
for i in s:
|
for item in s:
|
||||||
if i in "([{": # 当前是左括号,则入栈
|
if item == '(':
|
||||||
stack.append(i)
|
stack.append(')')
|
||||||
elif stack and stack[-1] == mapping[i]: # 当前是配对的右括号则出栈
|
elif item == '[':
|
||||||
stack.pop()
|
stack.append(']')
|
||||||
else: # 不是匹配的右括号或者没有左括号与之匹配,则返回false
|
elif item == '{':
|
||||||
|
stack.append('}')
|
||||||
|
elif not stack or stack[-1] != item:
|
||||||
return False
|
return False
|
||||||
return stack == [] # 最后必须正好把左括号匹配完
|
else:
|
||||||
|
stack.pop()
|
||||||
|
|
||||||
|
return True if not stack else False
|
||||||
|
```
|
||||||
|
|
||||||
|
```python3
|
||||||
|
# 方法二,使用字典
|
||||||
|
class Solution:
|
||||||
|
def isValid(self, s: str) -> bool:
|
||||||
|
stack = []
|
||||||
|
mapping = {
|
||||||
|
'(': ')',
|
||||||
|
'[': ']',
|
||||||
|
'{': '}'
|
||||||
|
}
|
||||||
|
for item in s:
|
||||||
|
if item in mapping.keys():
|
||||||
|
stack.append(mapping[item])
|
||||||
|
elif not stack or stack[-1] != item:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
stack.pop()
|
||||||
|
return True if not stack else False
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 24. 两两交换链表中的节点
|
## 24. 两两交换链表中的节点
|
||||||
|
|
||||||
https://leetcode-cn.com/problems/swap-nodes-in-pairs/
|
[力扣题目链接](https://leetcode-cn.com/problems/swap-nodes-in-pairs/)
|
||||||
|
|
||||||
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
|
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ https://leetcode-cn.com/problems/swap-nodes-in-pairs/
|
|||||||
|
|
||||||
建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。
|
建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。
|
||||||
|
|
||||||
对虚拟头结点的操作,还不熟悉的话,可以看这篇[链表:听说用虚拟头节点会方便很多?](https://mp.weixin.qq.com/s/L5aanfALdLEwVWGvyXPDqA)。
|
对虚拟头结点的操作,还不熟悉的话,可以看这篇[链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)。
|
||||||
|
|
||||||
接下来就是交换相邻两个元素了,**此时一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序**
|
接下来就是交换相邻两个元素了,**此时一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序**
|
||||||
|
|
||||||
@ -160,21 +160,29 @@ class Solution {
|
|||||||
|
|
||||||
Python:
|
Python:
|
||||||
```python
|
```python
|
||||||
|
# Definition for singly-linked list.
|
||||||
|
# class ListNode:
|
||||||
|
# def __init__(self, val=0, next=None):
|
||||||
|
# self.val = val
|
||||||
|
# self.next = next
|
||||||
|
|
||||||
class Solution:
|
class Solution:
|
||||||
def swapPairs(self, head: ListNode) -> ListNode:
|
def swapPairs(self, head: ListNode) -> ListNode:
|
||||||
dummy = ListNode(0) #设置一个虚拟头结点
|
res = ListNode(next=head)
|
||||||
dummy.next = head
|
pre = res
|
||||||
cur = dummy
|
|
||||||
while cur.next and cur.next.next:
|
# 必须有pre的下一个和下下个才能交换,否则说明已经交换结束了
|
||||||
tmp = cur.next #记录临时节点
|
while pre.next and pre.next.next:
|
||||||
tmp1 = cur.next.next.next #记录临时节点
|
cur = pre.next
|
||||||
|
post = pre.next.next
|
||||||
|
|
||||||
cur.next = cur.next.next #步骤一
|
# pre,cur,post对应最左,中间的,最右边的节点
|
||||||
cur.next.next = tmp #步骤二
|
cur.next = post.next
|
||||||
cur.next.next.next = tmp1 #步骤三
|
post.next = cur
|
||||||
|
pre.next = post
|
||||||
cur = cur.next.next #cur移动两位,准备下一轮交换
|
|
||||||
return dummy.next
|
pre = pre.next.next
|
||||||
|
return res.next
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
@ -248,6 +256,27 @@ fun swapPairs(head: ListNode?): ListNode? {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Swift:
|
||||||
|
```swift
|
||||||
|
func swapPairs(_ head: ListNode?) -> ListNode? {
|
||||||
|
if head == nil || head?.next == nil {
|
||||||
|
return head
|
||||||
|
}
|
||||||
|
let dummyHead: ListNode = ListNode(-1, head)
|
||||||
|
var current: ListNode? = dummyHead
|
||||||
|
while current?.next != nil && current?.next?.next != nil {
|
||||||
|
let temp1 = current?.next
|
||||||
|
let temp2 = current?.next?.next?.next
|
||||||
|
|
||||||
|
current?.next = current?.next?.next
|
||||||
|
current?.next?.next = temp1
|
||||||
|
current?.next?.next?.next = temp2
|
||||||
|
|
||||||
|
current = current?.next?.next
|
||||||
|
}
|
||||||
|
return dummyHead.next
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 27. 移除元素
|
## 27. 移除元素
|
||||||
|
|
||||||
题目地址:https://leetcode-cn.com/problems/remove-element/
|
[力扣题目链接](https://leetcode-cn.com/problems/remove-element/)
|
||||||
|
|
||||||
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
|
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
|
||||||
|
|
||||||
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
**要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。**
|
**要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。**
|
||||||
|
|
||||||
数组的基础知识可以看这里[程序员算法面试中,必须掌握的数组理论知识](https://mp.weixin.qq.com/s/c2KABb-Qgg66HrGf8z-8Og)。
|
数组的基础知识可以看这里[程序员算法面试中,必须掌握的数组理论知识](https://programmercarl.com/数组理论基础.html)。
|
||||||
|
|
||||||
### 暴力解法
|
### 暴力解法
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ public:
|
|||||||
* 时间复杂度:$O(n)$
|
* 时间复杂度:$O(n)$
|
||||||
* 空间复杂度:$O(1)$
|
* 空间复杂度:$O(1)$
|
||||||
|
|
||||||
旧文链接:[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
|
旧文链接:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)
|
||||||
|
|
||||||
## 相关题目推荐
|
## 相关题目推荐
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
# 28. 实现 strStr()
|
# 28. 实现 strStr()
|
||||||
|
|
||||||
https://leetcode-cn.com/problems/implement-strstr/
|
[力扣题目链接](https://leetcode-cn.com/problems/implement-strstr/)
|
||||||
|
|
||||||
实现 strStr() 函数。
|
实现 strStr() 函数。
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
# 31.下一个排列
|
# 31.下一个排列
|
||||||
|
|
||||||
链接:https://leetcode-cn.com/problems/next-permutation/
|
[力扣题目链接](https://leetcode-cn.com/problems/next-permutation/)
|
||||||
|
|
||||||
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
|
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
|
|
||||||
对二分还不了解的同学先做这两题:
|
对二分还不了解的同学先做这两题:
|
||||||
|
|
||||||
* [704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)
|
* [704.二分查找](https://programmercarl.com/0704.二分查找.html)
|
||||||
* [35.搜索插入位置](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)
|
* [35.搜索插入位置](https://programmercarl.com/0035.搜索插入位置.html)
|
||||||
|
|
||||||
下面我来把所有情况都讨论一下。
|
下面我来把所有情况都讨论一下。
|
||||||
|
|
||||||
@ -56,9 +56,9 @@
|
|||||||
|
|
||||||
## 寻找右边界
|
## 寻找右边界
|
||||||
|
|
||||||
先来寻找右边界,至于二分查找,如果看过[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。
|
先来寻找右边界,至于二分查找,如果看过[704.二分查找](https://programmercarl.com/0704.二分查找.html)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。
|
||||||
|
|
||||||
那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)这篇文章先看了,704题目做了之后再做这道题目就好很多了)
|
那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[704.二分查找](https://programmercarl.com/0704.二分查找.html)这篇文章先看了,704题目做了之后再做这道题目就好很多了)
|
||||||
|
|
||||||
确定好:计算出来的右边界是不包好target的右边界,左边界同理。
|
确定好:计算出来的右边界是不包好target的右边界,左边界同理。
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
# 35.搜索插入位置
|
# 35.搜索插入位置
|
||||||
|
|
||||||
题目地址:https://leetcode-cn.com/problems/search-insert-position/
|
[力扣题目链接](https://leetcode-cn.com/problems/search-insert-position/)
|
||||||
|
|
||||||
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
|
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ public:
|
|||||||
```
|
```
|
||||||
|
|
||||||
时间复杂度:O(n)
|
时间复杂度:O(n)
|
||||||
时间复杂度:O(1)
|
空间复杂度:O(1)
|
||||||
|
|
||||||
效率如下:
|
效率如下:
|
||||||
|
|
||||||
@ -238,16 +238,16 @@ Python:
|
|||||||
```python3
|
```python3
|
||||||
class Solution:
|
class Solution:
|
||||||
def searchInsert(self, nums: List[int], target: int) -> int:
|
def searchInsert(self, nums: List[int], target: int) -> int:
|
||||||
left, right = 0, len(nums) - 1
|
left, right = 0, len(nums) - 1
|
||||||
|
|
||||||
while left <= right:
|
while left <= right:
|
||||||
middle = (left + right) // 2
|
middle = (left + right) // 2
|
||||||
|
|
||||||
if nums[middle] < target:
|
if nums[middle] < target:
|
||||||
left = middle + 1
|
left = middle + 1
|
||||||
elif nums[middle] > target:
|
elif nums[middle] > target:
|
||||||
right = middle - 1
|
right = middle - 1
|
||||||
else:
|
else:
|
||||||
return middle
|
return middle
|
||||||
return right + 1
|
return right + 1
|
||||||
```
|
```
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## 37. 解数独
|
## 37. 解数独
|
||||||
|
|
||||||
题目地址:https://leetcode-cn.com/problems/sudoku-solver/
|
[力扣题目链接](https://leetcode-cn.com/problems/sudoku-solver/)
|
||||||
|
|
||||||
编写一个程序,通过填充空格来解决数独问题。
|
编写一个程序,通过填充空格来解决数独问题。
|
||||||
|
|
||||||
@ -40,11 +40,11 @@
|
|||||||
|
|
||||||
怎么做二维递归呢?
|
怎么做二维递归呢?
|
||||||
|
|
||||||
大家已经跟着「代码随想录」刷过了如下回溯法题目,例如:[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),[46.全排列(排列问题)](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw),以及[51.N皇后(N皇后问题)](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg),其实这些题目都是一维递归。
|
大家已经跟着「代码随想录」刷过了如下回溯法题目,例如:[77.组合(组合问题)](https://programmercarl.com/0077.组合.html),[131.分割回文串(分割问题)](https://programmercarl.com/0131.分割回文串.html),[78.子集(子集问题)](https://programmercarl.com/0078.子集.html),[46.全排列(排列问题)](https://programmercarl.com/0046.全排列.html),以及[51.N皇后(N皇后问题)](https://programmercarl.com/0051.N皇后.html),其实这些题目都是一维递归。
|
||||||
|
|
||||||
**如果以上这几道题目没有做过的话,不建议上来就做这道题哈!**
|
**如果以上这几道题目没有做过的话,不建议上来就做这道题哈!**
|
||||||
|
|
||||||
[N皇后问题](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg)是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来来遍历列,然后一行一列确定皇后的唯一位置。
|
[N皇后问题](https://programmercarl.com/0051.N皇后.html)是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来来遍历列,然后一行一列确定皇后的唯一位置。
|
||||||
|
|
||||||
本题就不一样了,**本题中棋盘的每一个位置都要放一个数字,并检查数字是否合法,解数独的树形结构要比N皇后更宽更深**。
|
本题就不一样了,**本题中棋盘的每一个位置都要放一个数字,并检查数字是否合法,解数独的树形结构要比N皇后更宽更深**。
|
||||||
|
|
||||||
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
**递归函数的返回值需要是bool类型,为什么呢?**
|
**递归函数的返回值需要是bool类型,为什么呢?**
|
||||||
|
|
||||||
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,这一点在[回溯算法:N皇后问题](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg)中已经介绍过了,一样的道理。
|
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,这一点在[回溯算法:N皇后问题](https://programmercarl.com/0051.N皇后.html)中已经介绍过了,一样的道理。
|
||||||
|
|
||||||
代码如下:
|
代码如下:
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 39. 组合总和
|
## 39. 组合总和
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/combination-sum/
|
[力扣题目链接](https://leetcode-cn.com/problems/combination-sum/)
|
||||||
|
|
||||||
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
||||||
|
|
||||||
@ -44,14 +44,14 @@ candidates 中的数字可以无限制重复被选取。
|
|||||||
|
|
||||||
题目中的**无限制重复被选取,吓得我赶紧想想 出现0 可咋办**,然后看到下面提示:1 <= candidates[i] <= 200,我就放心了。
|
题目中的**无限制重复被选取,吓得我赶紧想想 出现0 可咋办**,然后看到下面提示:1 <= candidates[i] <= 200,我就放心了。
|
||||||
|
|
||||||
本题和[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)和区别是:本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。
|
本题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html),[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)和区别是:本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。
|
||||||
|
|
||||||
本题搜索的过程抽象成树形结构如下:
|
本题搜索的过程抽象成树形结构如下:
|
||||||
|
|
||||||

|

|
||||||
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!
|
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!
|
||||||
|
|
||||||
而在[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)和[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w) 中都可以知道要递归K层,因为要取k个元素的组合。
|
而在[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html) 中都可以知道要递归K层,因为要取k个元素的组合。
|
||||||
|
|
||||||
## 回溯三部曲
|
## 回溯三部曲
|
||||||
|
|
||||||
@ -65,9 +65,9 @@ candidates 中的数字可以无限制重复被选取。
|
|||||||
|
|
||||||
**本题还需要startIndex来控制for循环的起始位置,对于组合问题,什么时候需要startIndex呢?**
|
**本题还需要startIndex来控制for循环的起始位置,对于组合问题,什么时候需要startIndex呢?**
|
||||||
|
|
||||||
我举过例子,如果是一个集合来求组合的话,就需要startIndex,例如:[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)。
|
我举过例子,如果是一个集合来求组合的话,就需要startIndex,例如:[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html),[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)。
|
||||||
|
|
||||||
如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:[回溯算法:电话号码的字母组合](https://mp.weixin.qq.com/s/e2ua2cmkE_vpYjM3j6HY0A)
|
如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:[回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
|
||||||
|
|
||||||
**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我再讲解排列的时候就重点介绍**。
|
**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我再讲解排列的时候就重点介绍**。
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ if (sum == target) {
|
|||||||
|
|
||||||
单层for循环依然是从startIndex开始,搜索candidates集合。
|
单层for循环依然是从startIndex开始,搜索candidates集合。
|
||||||
|
|
||||||
**注意本题和[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)、[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)的一个区别是:本题元素为可重复选取的**。
|
**注意本题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)、[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)的一个区别是:本题元素为可重复选取的**。
|
||||||
|
|
||||||
如何重复选取呢,看代码,注释部分:
|
如何重复选取呢,看代码,注释部分:
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ for (int i = startIndex; i < candidates.size(); i++) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中给出的模板,不难写出如下C++完整代码:
|
按照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中给出的模板,不难写出如下C++完整代码:
|
||||||
|
|
||||||
```CPP
|
```CPP
|
||||||
// 版本一
|
// 版本一
|
||||||
@ -213,14 +213,14 @@ public:
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
本题和我们之前讲过的[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)、[回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)有两点不同:
|
本题和我们之前讲过的[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)、[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)有两点不同:
|
||||||
|
|
||||||
* 组合没有数量要求
|
* 组合没有数量要求
|
||||||
* 元素可无限重复选取
|
* 元素可无限重复选取
|
||||||
|
|
||||||
针对这两个问题,我都做了详细的分析。
|
针对这两个问题,我都做了详细的分析。
|
||||||
|
|
||||||
并且给出了对于组合问题,什么时候用startIndex,什么时候不用,并用[回溯算法:电话号码的字母组合](https://mp.weixin.qq.com/s/e2ua2cmkE_vpYjM3j6HY0A)做了对比。
|
并且给出了对于组合问题,什么时候用startIndex,什么时候不用,并用[回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)做了对比。
|
||||||
|
|
||||||
最后还给出了本题的剪枝优化,这个优化如果是初学者的话并不容易想到。
|
最后还给出了本题的剪枝优化,这个优化如果是初学者的话并不容易想到。
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## 40.组合总和II
|
## 40.组合总和II
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/combination-sum-ii/
|
[力扣题目链接](https://leetcode-cn.com/problems/combination-sum-ii/)
|
||||||
|
|
||||||
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
||||||
|
|
||||||
@ -44,12 +44,12 @@ candidates 中的每个数字在每个组合中只能使用一次。
|
|||||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
这道题目和[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)如下区别:
|
这道题目和[39.组合总和](https://programmercarl.com/0039.组合总和.html)如下区别:
|
||||||
|
|
||||||
1. 本题candidates 中的每个数字在每个组合中只能使用一次。
|
1. 本题candidates 中的每个数字在每个组合中只能使用一次。
|
||||||
2. 本题数组candidates的元素是有重复的,而[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)是无重复元素的数组candidates
|
2. 本题数组candidates的元素是有重复的,而[39.组合总和](https://programmercarl.com/0039.组合总和.html)是无重复元素的数组candidates
|
||||||
|
|
||||||
最后本题和[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)要求一样,解集不能包含重复的组合。
|
最后本题和[39.组合总和](https://programmercarl.com/0039.组合总和.html)要求一样,解集不能包含重复的组合。
|
||||||
|
|
||||||
**本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合**。
|
**本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合**。
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ candidates 中的每个数字在每个组合中只能使用一次。
|
|||||||
|
|
||||||
* **递归函数参数**
|
* **递归函数参数**
|
||||||
|
|
||||||
与[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
|
与[39.组合总和](https://programmercarl.com/0039.组合总和.html)套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
|
||||||
|
|
||||||
这个集合去重的重任就是used来完成的。
|
这个集合去重的重任就是used来完成的。
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ void backtracking(vector<int>& candidates, int target, int sum, int startIndex,
|
|||||||
|
|
||||||
* **递归终止条件**
|
* **递归终止条件**
|
||||||
|
|
||||||
与[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)相同,终止条件为 `sum > target` 和 `sum == target`。
|
与[39.组合总和](https://programmercarl.com/0039.组合总和.html)相同,终止条件为 `sum > target` 和 `sum == target`。
|
||||||
|
|
||||||
代码如下:
|
代码如下:
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ if (sum == target) {
|
|||||||
|
|
||||||
* **单层搜索的逻辑**
|
* **单层搜索的逻辑**
|
||||||
|
|
||||||
这里与[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)最大的不同就是要去重了。
|
这里与[39.组合总和](https://programmercarl.com/0039.组合总和.html)最大的不同就是要去重了。
|
||||||
|
|
||||||
前面我们提到:要去重的是“同一树层上的使用过”,如果判断同一树层上元素(相同的元素)是否使用过了呢。
|
前面我们提到:要去重的是“同一树层上的使用过”,如果判断同一树层上元素(相同的元素)是否使用过了呢。
|
||||||
|
|
||||||
@ -244,7 +244,7 @@ public:
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
本题同样是求组合总和,但就是因为其数组candidates有重复元素,而要求不能有重复的组合,所以相对于[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)难度提升了不少。
|
本题同样是求组合总和,但就是因为其数组candidates有重复元素,而要求不能有重复的组合,所以相对于[39.组合总和](https://programmercarl.com/0039.组合总和.html)难度提升了不少。
|
||||||
|
|
||||||
**关键是去重的逻辑,代码很简单,网上一搜一大把,但几乎没有能把这块代码含义讲明白的,基本都是给出代码,然后说这就是去重了,究竟怎么个去重法也是模棱两可**。
|
**关键是去重的逻辑,代码很简单,网上一搜一大把,但几乎没有能把这块代码含义讲明白的,基本都是给出代码,然后说这就是去重了,究竟怎么个去重法也是模棱两可**。
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
# 42. 接雨水
|
# 42. 接雨水
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/trapping-rain-water/
|
[力扣题目链接](https://leetcode-cn.com/problems/trapping-rain-water/)
|
||||||
|
|
||||||
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
|
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
|
||||||
|
|
||||||
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
* 输入:height = [4,2,0,3,2,5]
|
* 输入:height = [4,2,0,3,2,5]
|
||||||
* 输出:9
|
* 输出:9
|
||||||
|
|
||||||
|
|
||||||
# 思路
|
# 思路
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ public:
|
|||||||
|
|
||||||
这个解法可以说是最不好理解的了,所以下面我花了大量的篇幅来介绍这种方法。
|
这个解法可以说是最不好理解的了,所以下面我花了大量的篇幅来介绍这种方法。
|
||||||
|
|
||||||
单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://mp.weixin.qq.com/s/Xgcqx5eBa3xZabt_LurnNQ)一样,需要我们自己维持顺序,没有现成的容器可以用。
|
单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://programmercarl.com/0239.滑动窗口最大值.html)一样,需要我们自己维持顺序,没有现成的容器可以用。
|
||||||
|
|
||||||
|
|
||||||
### 准备工作
|
### 准备工作
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## 45.跳跃游戏II
|
## 45.跳跃游戏II
|
||||||
|
|
||||||
题目地址:https://leetcode-cn.com/problems/jump-game-ii/
|
[力扣题目链接](https://leetcode-cn.com/problems/jump-game-ii/)
|
||||||
|
|
||||||
给定一个非负整数数组,你最初位于数组的第一个位置。
|
给定一个非负整数数组,你最初位于数组的第一个位置。
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
本题相对于[55.跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)还是难了不少。
|
本题相对于[55.跳跃游戏](https://programmercarl.com/0055.跳跃游戏.html)还是难了不少。
|
||||||
|
|
||||||
但思路是相似的,还是要看最大覆盖范围。
|
但思路是相似的,还是要看最大覆盖范围。
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ public:
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
相信大家可以发现,这道题目相当于[55.跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不止一点。
|
相信大家可以发现,这道题目相当于[55.跳跃游戏](https://programmercarl.com/0055.跳跃游戏.html)难了不止一点。
|
||||||
|
|
||||||
但代码又十分简单,贪心就是这么巧妙。
|
但代码又十分简单,贪心就是这么巧妙。
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 46.全排列
|
## 46.全排列
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/permutations/
|
[力扣题目链接](https://leetcode-cn.com/problems/permutations/)
|
||||||
|
|
||||||
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
|
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
|
||||||
|
|
||||||
@ -30,11 +30,11 @@
|
|||||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](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),接下来看一看排列问题。
|
此时我们已经学习了[77.组合问题](https://programmercarl.com/0077.组合.html)、 [131.分割回文串](https://programmercarl.com/0131.分割回文串.html)和[78.子集问题](https://programmercarl.com/0078.子集.html),接下来看一看排列问题。
|
||||||
|
|
||||||
相信这个排列问题就算是让你用for循环暴力把结果搜索出来,这个暴力也不是很好写。
|
相信这个排列问题就算是让你用for循环暴力把结果搜索出来,这个暴力也不是很好写。
|
||||||
|
|
||||||
所以正如我们在[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)所讲的为什么回溯法是暴力搜索,效率这么低,还要用它?
|
所以正如我们在[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)所讲的为什么回溯法是暴力搜索,效率这么低,还要用它?
|
||||||
|
|
||||||
**因为一些问题能暴力搜出来就已经很不错了!**
|
**因为一些问题能暴力搜出来就已经很不错了!**
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ if (path.size() == nums.size()) {
|
|||||||
|
|
||||||
* 单层搜索的逻辑
|
* 单层搜索的逻辑
|
||||||
|
|
||||||
这里和[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了。
|
这里和[77.组合问题](https://programmercarl.com/0077.组合.html)、[131.切割问题](https://programmercarl.com/0131.分割回文串.html)和[78.子集问题](https://programmercarl.com/0078.子集.html)最大的不同就是for循环里不用startIndex了。
|
||||||
|
|
||||||
因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。
|
因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
## 47.全排列 II
|
## 47.全排列 II
|
||||||
|
|
||||||
题目链接:https://leetcode-cn.com/problems/permutations-ii/
|
[力扣题目链接](https://leetcode-cn.com/problems/permutations-ii/)
|
||||||
|
|
||||||
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
|
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
|
||||||
|
|
||||||
@ -33,11 +33,11 @@
|
|||||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||||
|
|
||||||
|
|
||||||
这道题目和[回溯算法:排列问题!](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。
|
这道题目和[回溯算法:排列问题!](https://programmercarl.com/0046.全排列.html)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。
|
||||||
|
|
||||||
这里又涉及到去重了。
|
这里又涉及到去重了。
|
||||||
|
|
||||||
在[回溯算法:求组合总和(三)](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ) 、[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)我们分别详细讲解了组合问题和子集问题如何去重。
|
在[回溯算法:求组合总和(三)](https://programmercarl.com/0040.组合总和II.html) 、[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)我们分别详细讲解了组合问题和子集问题如何去重。
|
||||||
|
|
||||||
那么排列问题其实也是一样的套路。
|
那么排列问题其实也是一样的套路。
|
||||||
|
|
||||||
@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
**一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果**。
|
**一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果**。
|
||||||
|
|
||||||
在[回溯算法:排列问题!](https://mp.weixin.qq.com/s/SCOjeMX1t41wcvJq49GhMw)中已经详解讲解了排列问题的写法,在[回溯算法:求组合总和(三)](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ) 、[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)中详细讲解的去重的写法,所以这次我就不用回溯三部曲分析了,直接给出代码,如下:
|
在[回溯算法:排列问题!](https://programmercarl.com/0046.全排列.html)中已经详解讲解了排列问题的写法,在[回溯算法:求组合总和(三)](https://programmercarl.com/0040.组合总和II.html) 、[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)中详细讲解的去重的写法,所以这次我就不用回溯三部曲分析了,直接给出代码,如下:
|
||||||
|
|
||||||
## C++代码
|
## C++代码
|
||||||
|
|
||||||
|
@ -88,7 +88,8 @@
|
|||||||
|
|
||||||
|
|
||||||
以上分析完毕,C++代码如下:
|
以上分析完毕,C++代码如下:
|
||||||
```
|
|
||||||
|
```cpp
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
int climbStairs(int n) {
|
int climbStairs(int n) {
|
||||||
|
@ -195,6 +195,40 @@ public:
|
|||||||
|
|
||||||
Java:
|
Java:
|
||||||
|
|
||||||
|
动态规划
|
||||||
|
```java
|
||||||
|
class Solution {
|
||||||
|
public int largestRectangleArea(int[] heights) {
|
||||||
|
int length = heights.length;
|
||||||
|
int[] minLeftIndex = new int [length];
|
||||||
|
int[] maxRigthIndex = new int [length];
|
||||||
|
|
||||||
|
// 记录左边第一个小于该柱子的下标
|
||||||
|
minLeftIndex[0] = -1 ;
|
||||||
|
for (int i = 1; i < length; i++) {
|
||||||
|
int t = i - 1;
|
||||||
|
// 这里不是用if,而是不断向右寻找的过程
|
||||||
|
while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
|
||||||
|
minLeftIndex[i] = t;
|
||||||
|
}
|
||||||
|
// 记录每个柱子 右边第一个小于该柱子的下标
|
||||||
|
maxRigthIndex[length - 1] = length;
|
||||||
|
for (int i = length - 2; i >= 0; i--) {
|
||||||
|
int t = i + 1;
|
||||||
|
while(t < length && heights[t] >= heights[i]) t = maxRigthIndex[t];
|
||||||
|
maxRigthIndex[i] = t;
|
||||||
|
}
|
||||||
|
// 求和
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int sum = heights[i] * (maxRigthIndex[i] - minLeftIndex[i] - 1);
|
||||||
|
result = Math.max(sum, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
动态规划
|
动态规划
|
||||||
|
@ -1072,6 +1072,32 @@ class Solution {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
//方法二:用一个max变量来保存最大值
|
||||||
|
class Solution {
|
||||||
|
public List<Integer> largestValues(TreeNode root) {
|
||||||
|
Queue<TreeNode> queue = new LinkedList<TreeNode>();
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
if (root != null) queue.add(root);
|
||||||
|
|
||||||
|
while(!queue.isEmpty()){
|
||||||
|
int size = queue.size();
|
||||||
|
int max = Integer.MIN_VALUE; // 初始化为最小值
|
||||||
|
for(int i = 0; i < size; i++){
|
||||||
|
TreeNode node = queue.poll();
|
||||||
|
max = Math.max(node.val, max);
|
||||||
|
if(node.left != null)
|
||||||
|
queue.add(node.left);
|
||||||
|
if(node.right != null)
|
||||||
|
queue.add(node.right);
|
||||||
|
}
|
||||||
|
result.add(max); // 取最大值放进数组
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
go:
|
go:
|
||||||
|
|
||||||
```GO
|
```GO
|
||||||
@ -1129,7 +1155,7 @@ var largestValues = function(root) {
|
|||||||
queue.push(root);
|
queue.push(root);
|
||||||
while(root!==null&&queue.length){
|
while(root!==null&&queue.length){
|
||||||
//设置max初始值就是队列的第一个元素
|
//设置max初始值就是队列的第一个元素
|
||||||
let max=queue[0];
|
let max=queue[0].val;
|
||||||
let length=queue.length;
|
let length=queue.length;
|
||||||
while(length--){
|
while(length--){
|
||||||
let node = queue.shift();
|
let node = queue.shift();
|
||||||
@ -1532,6 +1558,29 @@ Java:
|
|||||||
|
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
```python 3
|
||||||
|
class Solution:
|
||||||
|
def maxDepth(self, root: TreeNode) -> int:
|
||||||
|
if root == None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
queue_ = [root]
|
||||||
|
result = []
|
||||||
|
while queue_:
|
||||||
|
length = len(queue_)
|
||||||
|
sub = []
|
||||||
|
for i in range(length):
|
||||||
|
cur = queue_.pop(0)
|
||||||
|
sub.append(cur.val)
|
||||||
|
#子节点入队列
|
||||||
|
if cur.left: queue_.append(cur.left)
|
||||||
|
if cur.right: queue_.append(cur.right)
|
||||||
|
result.append(sub)
|
||||||
|
|
||||||
|
|
||||||
|
return len(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
|
||||||
@ -1539,6 +1588,8 @@ JavaScript:
|
|||||||
|
|
||||||
# 111.二叉树的最小深度
|
# 111.二叉树的最小深度
|
||||||
|
|
||||||
|
题目地址:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
|
||||||
|
|
||||||
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
|
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
|
||||||
|
|
||||||
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
|
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
|
||||||
@ -1572,9 +1623,74 @@ public:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Java:
|
Java:
|
||||||
|
```java
|
||||||
|
class Solution {
|
||||||
|
|
||||||
|
public int minDepth(TreeNode root){
|
||||||
|
if (root == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Queue<TreeNode> queue = new LinkedList<>();
|
||||||
|
queue.offer(root);
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
while (!queue.isEmpty()){
|
||||||
|
|
||||||
|
int size = queue.size();
|
||||||
|
depth++;
|
||||||
|
|
||||||
|
TreeNode cur = null;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
cur = queue.poll();
|
||||||
|
|
||||||
|
//如果当前节点的左右孩子都为空,直接返回最小深度
|
||||||
|
if (cur.left == null && cur.right == null){
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.left != null) queue.offer(cur.left);
|
||||||
|
if (cur.right != null) queue.offer(cur.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Python:
|
}
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Python 3:
|
||||||
|
|
||||||
|
```python 3
|
||||||
|
# Definition for a binary tree node.
|
||||||
|
# class TreeNode:
|
||||||
|
# def __init__(self, val=0, left=None, right=None):
|
||||||
|
# self.val = val
|
||||||
|
# self.left = left
|
||||||
|
# self.right = right
|
||||||
|
class Solution:
|
||||||
|
def minDepth(self, root: TreeNode) -> int:
|
||||||
|
if root == None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
#根节点的深度为1
|
||||||
|
queue_ = [(root,1)]
|
||||||
|
while queue_:
|
||||||
|
cur, depth = queue_.pop(0)
|
||||||
|
|
||||||
|
if cur.left == None and cur.right == None:
|
||||||
|
return depth
|
||||||
|
#先左子节点,由于左子节点没有孩子,则就是这一层了
|
||||||
|
if cur.left:
|
||||||
|
queue_.append((cur.left,depth + 1))
|
||||||
|
if cur.right:
|
||||||
|
queue_.append((cur.right,depth + 1))
|
||||||
|
|
||||||
|
return 0
|
||||||
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
|
||||||
|
@ -223,17 +223,19 @@ var evalRPN = function(tokens) {
|
|||||||
python3
|
python3
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def evalRPN(tokens) -> int:
|
class Solution:
|
||||||
stack = list()
|
def evalRPN(self, tokens: List[str]) -> int:
|
||||||
for i in range(len(tokens)):
|
stack = []
|
||||||
if tokens[i] not in ["+", "-", "*", "/"]:
|
for item in tokens:
|
||||||
stack.append(tokens[i])
|
if item not in {"+", "-", "*", "/"}:
|
||||||
else:
|
stack.append(item)
|
||||||
tmp1 = stack.pop()
|
else:
|
||||||
tmp2 = stack.pop()
|
first_num, second_num = stack.pop(), stack.pop()
|
||||||
res = eval(tmp2+tokens[i]+tmp1)
|
stack.append(
|
||||||
stack.append(str(int(res)))
|
int(eval(f'{second_num} {item} {first_num}')) # 第一个出来的在运算符后面
|
||||||
return stack[-1]
|
)
|
||||||
|
return int(stack.pop()) # 如果一开始只有一个数,那么会是字符串形式的
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ class Solution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 版本二: 空间优化
|
// 版本二: 二维 dp数组
|
||||||
class Solution {
|
class Solution {
|
||||||
public int maxProfit(int k, int[] prices) {
|
public int maxProfit(int k, int[] prices) {
|
||||||
if (prices.length == 0) return 0;
|
if (prices.length == 0) return 0;
|
||||||
@ -220,6 +220,25 @@ class Solution {
|
|||||||
return dp[len - 1][k*2];
|
return dp[len - 1][k*2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//版本三:一维 dp数组
|
||||||
|
class Solution {
|
||||||
|
public int maxProfit(int k, int[] prices) {
|
||||||
|
//在版本二的基础上,由于我们只关心前一天的股票买入情况,所以只存储前一天的股票买入情况
|
||||||
|
if(prices.length==0)return 0;
|
||||||
|
int[] dp=new int[2*k+1];
|
||||||
|
for (int i = 1; i <2*k ; i+=2) {
|
||||||
|
dp[i]=-prices[0];
|
||||||
|
}
|
||||||
|
for (int i = 0; i <prices.length ; i++) {
|
||||||
|
for (int j = 1; j <2*k ; j+=2) {
|
||||||
|
dp[j]=Math.max(dp[j],dp[j-1]-prices[i]);
|
||||||
|
dp[j+1]=Math.max(dp[j+1],dp[j]+prices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[2*k];
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -245,13 +245,15 @@ Python:
|
|||||||
# def __init__(self, val=0, next=None):
|
# def __init__(self, val=0, next=None):
|
||||||
# self.val = val
|
# self.val = val
|
||||||
# self.next = next
|
# self.next = next
|
||||||
|
|
||||||
class Solution:
|
class Solution:
|
||||||
def removeElements(self, head: ListNode, val: int) -> ListNode:
|
def removeElements(self, head: ListNode, val: int) -> ListNode:
|
||||||
dummy_head = ListNode(next=head) #添加一个虚拟节点
|
dummy_head = ListNode(next=head)
|
||||||
cur = dummy_head
|
cur = dummy_head
|
||||||
while(cur.next!=None):
|
|
||||||
if(cur.next.val == val):
|
while cur.next:
|
||||||
cur.next = cur.next.next #删除cur.next节点
|
if cur.next.val == val:
|
||||||
|
cur.next = cur.next.next # 删除下一个节点
|
||||||
else:
|
else:
|
||||||
cur = cur.next
|
cur = cur.next
|
||||||
return dummy_head.next
|
return dummy_head.next
|
||||||
|
@ -294,53 +294,66 @@ Python:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
class MyStack:
|
class MyStack:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Initialize your data structure here.
|
Python普通的Queue或SimpleQueue没有类似于peek的功能
|
||||||
|
也无法用索引访问,在实现top的时候较为困难。
|
||||||
|
|
||||||
|
用list可以,但是在使用pop(0)的时候时间复杂度为O(n)
|
||||||
|
因此这里使用双向队列,我们保证只执行popleft()和append(),因为deque可以用索引访问,可以实现和peek相似的功能
|
||||||
|
|
||||||
|
in - 存所有数据
|
||||||
|
out - 仅在pop的时候会用到
|
||||||
"""
|
"""
|
||||||
#使用两个队列来实现
|
self.queue_in = deque()
|
||||||
self.que1 = deque()
|
self.queue_out = deque()
|
||||||
self.que2 = deque()
|
|
||||||
|
|
||||||
def push(self, x: int) -> None:
|
def push(self, x: int) -> None:
|
||||||
"""
|
"""
|
||||||
Push element x onto stack.
|
直接append即可
|
||||||
"""
|
"""
|
||||||
self.que1.append(x)
|
self.queue_in.append(x)
|
||||||
|
|
||||||
|
|
||||||
def pop(self) -> int:
|
def pop(self) -> int:
|
||||||
"""
|
"""
|
||||||
Removes the element on top of the stack and returns that element.
|
1. 首先确认不空
|
||||||
|
2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out
|
||||||
|
3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out
|
||||||
|
4. 交换in和out,此时out里只有一个元素
|
||||||
|
5. 把out中的pop出来,即是原队列的最后一个
|
||||||
|
|
||||||
|
tip:这不能像栈实现队列一样,因为另一个queue也是FIFO,如果执行pop()它不能像
|
||||||
|
stack一样从另一个pop(),所以干脆in只用来存数据,pop()的时候两个进行交换
|
||||||
"""
|
"""
|
||||||
size = len(self.que1)
|
if self.empty():
|
||||||
size -= 1#这里先减一是为了保证最后面的元素
|
return None
|
||||||
while size > 0:
|
|
||||||
size -= 1
|
|
||||||
self.que2.append(self.que1.popleft())
|
|
||||||
|
|
||||||
|
for i in range(len(self.queue_in) - 1):
|
||||||
result = self.que1.popleft()
|
self.queue_out.append(self.queue_in.popleft())
|
||||||
self.que1, self.que2= self.que2, self.que1#将que2和que1交换 que1经过之前的操作应该是空了
|
|
||||||
#一定注意不能直接使用que1 = que2 这样que2的改变会影响que1 可以用浅拷贝
|
self.queue_in, self.queue_out = self.queue_out, self.queue_in # 交换in和out,这也是为啥in只用来存
|
||||||
return result
|
return self.queue_out.popleft()
|
||||||
|
|
||||||
def top(self) -> int:
|
def top(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the top element.
|
1. 首先确认不空
|
||||||
|
2. 我们仅有in会存放数据,所以返回第一个即可
|
||||||
"""
|
"""
|
||||||
return self.que1[-1]
|
if self.empty():
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.queue_in[-1]
|
||||||
|
|
||||||
|
|
||||||
def empty(self) -> bool:
|
def empty(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns whether the stack is empty.
|
因为只有in存了数据,只要判断in是不是有数即可
|
||||||
"""
|
"""
|
||||||
#print(self.que1)
|
return len(self.queue_in) == 0
|
||||||
if len(self.que1) == 0:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -281,48 +281,60 @@ class MyQueue {
|
|||||||
|
|
||||||
Python:
|
Python:
|
||||||
```python
|
```python
|
||||||
# 使用两个栈实现先进先出的队列
|
|
||||||
class MyQueue:
|
class MyQueue:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Initialize your data structure here.
|
in主要负责push,out主要负责pop
|
||||||
"""
|
"""
|
||||||
self.stack1 = list()
|
self.stack_in = []
|
||||||
self.stack2 = list()
|
self.stack_out = []
|
||||||
|
|
||||||
|
|
||||||
def push(self, x: int) -> None:
|
def push(self, x: int) -> None:
|
||||||
"""
|
"""
|
||||||
Push element x to the back of queue.
|
有新元素进来,就往in里面push
|
||||||
"""
|
"""
|
||||||
# self.stack1用于接受元素
|
self.stack_in.append(x)
|
||||||
self.stack1.append(x)
|
|
||||||
|
|
||||||
def pop(self) -> int:
|
def pop(self) -> int:
|
||||||
"""
|
"""
|
||||||
Removes the element from in front of queue and returns that element.
|
1. 检查如果out里面元素,则直接pop
|
||||||
|
2. 如果out没有元素,就把in里面的元素(除了第一个)依次pop后装进out里面
|
||||||
|
3. 直接把in剩下的元素pop出来,就是queue头部的
|
||||||
"""
|
"""
|
||||||
# self.stack2用于弹出元素,如果self.stack2为[],则将self.stack1中元素全部弹出给self.stack2
|
if self.empty:
|
||||||
if self.stack2 == []:
|
return None
|
||||||
while self.stack1:
|
|
||||||
tmp = self.stack1.pop()
|
if self.stack_out:
|
||||||
self.stack2.append(tmp)
|
return self.stack_out.pop()
|
||||||
return self.stack2.pop()
|
else:
|
||||||
|
for i in range(1, len(self.stack_in)):
|
||||||
|
self.stack_out.append(self.stack_in.pop())
|
||||||
|
return self.stack_in.pop()
|
||||||
|
|
||||||
|
|
||||||
def peek(self) -> int:
|
def peek(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the front element.
|
1. 查out有没有元素,有就把最上面的返回
|
||||||
|
2. 如果out没有元素,就把in最下面的返回
|
||||||
"""
|
"""
|
||||||
if self.stack2 == []:
|
if self.empty:
|
||||||
while self.stack1:
|
return None
|
||||||
tmp = self.stack1.pop()
|
|
||||||
self.stack2.append(tmp)
|
if self.stack_out:
|
||||||
return self.stack2[-1]
|
return self.stack_out[-1]
|
||||||
|
else:
|
||||||
|
return self.stack_in[0]
|
||||||
|
|
||||||
|
|
||||||
def empty(self) -> bool:
|
def empty(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns whether the queue is empty.
|
只要in或者out有元素,说明队列不为空
|
||||||
"""
|
"""
|
||||||
return self.stack1 == [] and self.stack2 == []
|
return not (self.stack_in or self.stack_out)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,6 +161,7 @@ public:
|
|||||||
Java:
|
Java:
|
||||||
```Java
|
```Java
|
||||||
class Solution {
|
class Solution {
|
||||||
|
// 版本一,先遍历物品, 再遍历背包
|
||||||
public int numSquares(int n) {
|
public int numSquares(int n) {
|
||||||
int max = Integer.MAX_VALUE;
|
int max = Integer.MAX_VALUE;
|
||||||
int[] dp = new int[n + 1];
|
int[] dp = new int[n + 1];
|
||||||
@ -170,7 +171,9 @@ class Solution {
|
|||||||
}
|
}
|
||||||
//当和为0时,组合的个数为0
|
//当和为0时,组合的个数为0
|
||||||
dp[0] = 0;
|
dp[0] = 0;
|
||||||
|
// 遍历物品
|
||||||
for (int i = 1; i * i <= n; i++) {
|
for (int i = 1; i * i <= n; i++) {
|
||||||
|
// 遍历背包
|
||||||
for (int j = i * i; j <= n; j++) {
|
for (int j = i * i; j <= n; j++) {
|
||||||
if (dp[j - i * i] != max) {
|
if (dp[j - i * i] != max) {
|
||||||
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
|
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
|
||||||
@ -180,6 +183,28 @@ class Solution {
|
|||||||
return dp[n];
|
return dp[n];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Solution {
|
||||||
|
// 版本二, 先遍历背包, 再遍历物品
|
||||||
|
public int numSquares(int n) {
|
||||||
|
int max = Integer.MAX_VALUE;
|
||||||
|
int[] dp = new int[n + 1];
|
||||||
|
// 初始化
|
||||||
|
for (int j = 0; j <= n; j++) {
|
||||||
|
dp[j] = max;
|
||||||
|
}
|
||||||
|
// 当和为0时,组合的个数为0
|
||||||
|
dp[0] = 0;
|
||||||
|
// 遍历背包
|
||||||
|
for (int j = 1; j <= n; j++) {
|
||||||
|
// 遍历物品
|
||||||
|
for (int i = 1; i * i <= j; i++) {
|
||||||
|
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
@ -187,7 +212,7 @@ Python:
|
|||||||
```python3
|
```python3
|
||||||
class Solution:
|
class Solution:
|
||||||
def numSquares(self, n: int) -> int:
|
def numSquares(self, n: int) -> int:
|
||||||
'''版本一'''
|
'''版本一,先遍历背包, 再遍历物品'''
|
||||||
# 初始化
|
# 初始化
|
||||||
nums = [i**2 for i in range(1, n + 1) if i**2 <= n]
|
nums = [i**2 for i in range(1, n + 1) if i**2 <= n]
|
||||||
dp = [10**4]*(n + 1)
|
dp = [10**4]*(n + 1)
|
||||||
@ -201,7 +226,7 @@ class Solution:
|
|||||||
return dp[n]
|
return dp[n]
|
||||||
|
|
||||||
def numSquares1(self, n: int) -> int:
|
def numSquares1(self, n: int) -> int:
|
||||||
'''版本二'''
|
'''版本二, 先遍历物品, 再遍历背包'''
|
||||||
# 初始化
|
# 初始化
|
||||||
nums = [i**2 for i in range(1, n + 1) if i**2 <= n]
|
nums = [i**2 for i in range(1, n + 1) if i**2 <= n]
|
||||||
dp = [10**4]*(n + 1)
|
dp = [10**4]*(n + 1)
|
||||||
@ -217,6 +242,22 @@ class Solution:
|
|||||||
Python3:
|
Python3:
|
||||||
```python
|
```python
|
||||||
class Solution:
|
class Solution:
|
||||||
|
'''版本一,先遍历背包, 再遍历物品'''
|
||||||
|
def numSquares(self, n: int) -> int:
|
||||||
|
dp = [n] * (n + 1)
|
||||||
|
dp[0] = 0
|
||||||
|
# 遍历背包
|
||||||
|
for j in range(1, n+1):
|
||||||
|
for i in range(1, n):
|
||||||
|
num = i ** 2
|
||||||
|
if num > j: break
|
||||||
|
# 遍历物品
|
||||||
|
if j - num >= 0:
|
||||||
|
dp[j] = min(dp[j], dp[j - num] + 1)
|
||||||
|
return dp[n]
|
||||||
|
|
||||||
|
class Solution:
|
||||||
|
'''版本二, 先遍历物品, 再遍历背包'''
|
||||||
def numSquares(self, n: int) -> int:
|
def numSquares(self, n: int) -> int:
|
||||||
# 初始化
|
# 初始化
|
||||||
# 组成和的完全平方数的最多个数,就是只用1构成
|
# 组成和的完全平方数的最多个数,就是只用1构成
|
||||||
|
@ -162,21 +162,14 @@ class Solution:
|
|||||||
Do not return anything, modify s in-place instead.
|
Do not return anything, modify s in-place instead.
|
||||||
"""
|
"""
|
||||||
left, right = 0, len(s) - 1
|
left, right = 0, len(s) - 1
|
||||||
while(left < right):
|
|
||||||
|
# 该方法已经不需要判断奇偶数,经测试后时间空间复杂度比用 for i in range(right//2)更低
|
||||||
|
# 推荐该写法,更加通俗易懂
|
||||||
|
while left < right:
|
||||||
s[left], s[right] = s[right], s[left]
|
s[left], s[right] = s[right], s[left]
|
||||||
left += 1
|
left += 1
|
||||||
right -= 1
|
right -= 1
|
||||||
|
|
||||||
# 下面的写法更加简洁,但是都是同样的算法
|
|
||||||
# class Solution:
|
|
||||||
# def reverseString(self, s: List[str]) -> None:
|
|
||||||
# """
|
|
||||||
# Do not return anything, modify s in-place instead.
|
|
||||||
# """
|
|
||||||
# 不需要判别是偶数个还是奇数个序列,因为奇数个的时候,中间那个不需要交换就可
|
|
||||||
# for i in range(len(s)//2):
|
|
||||||
# s[i], s[len(s)-1-i] = s[len(s)-1-i], s[i]
|
|
||||||
# return s
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
@ -143,6 +143,26 @@ func intersection(nums1 []int, nums2 []int) []int {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
```golang
|
||||||
|
//优化版,利用set,减少count统计
|
||||||
|
func intersection(nums1 []int, nums2 []int) []int {
|
||||||
|
set:=make(map[int]struct{},0)
|
||||||
|
res:=make([]int,0)
|
||||||
|
for _,v:=range nums1{
|
||||||
|
if _,ok:=set[v];!ok{
|
||||||
|
set[v]=struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _,v:=range nums2{
|
||||||
|
//如果存在于上一个数组中,则加入结果集,并清空该set值
|
||||||
|
if _,ok:=set[v];ok{
|
||||||
|
res=append(res,v)
|
||||||
|
delete(set, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
javaScript:
|
javaScript:
|
||||||
|
|
||||||
|
@ -19,15 +19,15 @@
|
|||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
输入:nums: [1, 1, 1, 1, 1], S: 3
|
输入:nums: [1, 1, 1, 1, 1], S: 3
|
||||||
输出:5
|
输出:5
|
||||||
|
|
||||||
解释:
|
解释:
|
||||||
-1+1+1+1+1 = 3
|
-1+1+1+1+1 = 3
|
||||||
+1-1+1+1+1 = 3
|
+1-1+1+1+1 = 3
|
||||||
+1+1-1+1+1 = 3
|
+1+1-1+1+1 = 3
|
||||||
+1+1+1-1+1 = 3
|
+1+1+1-1+1 = 3
|
||||||
+1+1+1+1-1 = 3
|
+1+1+1+1-1 = 3
|
||||||
|
|
||||||
一共有5种方法让最终目标和为3。
|
一共有5种方法让最终目标和为3。
|
||||||
|
|
||||||
@ -202,6 +202,7 @@ public:
|
|||||||
for (int i = 0; i < nums.size(); i++) sum += nums[i];
|
for (int i = 0; i < nums.size(); i++) sum += nums[i];
|
||||||
if (S > sum) return 0; // 此时没有方案
|
if (S > sum) return 0; // 此时没有方案
|
||||||
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
|
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
|
||||||
|
if (S + sum < 0) return 0; // 以确保bagSize为正数
|
||||||
int bagSize = (S + sum) / 2;
|
int bagSize = (S + sum) / 2;
|
||||||
vector<int> dp(bagSize + 1, 0);
|
vector<int> dp(bagSize + 1, 0);
|
||||||
dp[0] = 1;
|
dp[0] = 1;
|
||||||
@ -311,7 +312,7 @@ Javascript:
|
|||||||
const findTargetSumWays = (nums, target) => {
|
const findTargetSumWays = (nums, target) => {
|
||||||
|
|
||||||
const sum = nums.reduce((a, b) => a+b);
|
const sum = nums.reduce((a, b) => a+b);
|
||||||
|
|
||||||
if(target > sum) {
|
if(target > sum) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -155,34 +155,27 @@ class Solution {
|
|||||||
|
|
||||||
Python:
|
Python:
|
||||||
```python
|
```python
|
||||||
|
class Solution:
|
||||||
class Solution(object):
|
def reverseStr(self, s: str, k: int) -> str:
|
||||||
def reverseStr(self, s, k):
|
|
||||||
"""
|
"""
|
||||||
:type s: str
|
1. 使用range(start, end, step)来确定需要调换的初始位置
|
||||||
:type k: int
|
2. 对于字符串s = 'abc',如果使用s[0:999] ===> 'abc'。字符串末尾如果超过最大长度,则会返回至字符串最后一个值,这个特性可以避免一些边界条件的处理。
|
||||||
:rtype: str
|
3. 用切片整体替换,而不是一个个替换.
|
||||||
"""
|
"""
|
||||||
from functools import reduce
|
def reverse_substring(text):
|
||||||
# turn s into a list
|
left, right = 0, len(text) - 1
|
||||||
s = list(s)
|
|
||||||
|
|
||||||
# another way to simply use a[::-1], but i feel this is easier to understand
|
|
||||||
def reverse(s):
|
|
||||||
left, right = 0, len(s) - 1
|
|
||||||
while left < right:
|
while left < right:
|
||||||
s[left], s[right] = s[right], s[left]
|
text[left], text[right] = text[right], text[left]
|
||||||
left += 1
|
left += 1
|
||||||
right -= 1
|
right -= 1
|
||||||
return s
|
return text
|
||||||
|
|
||||||
# make sure we reverse each 2k elements
|
res = list(s)
|
||||||
for i in range(0, len(s), 2*k):
|
|
||||||
s[i:(i+k)] = reverse(s[i:(i+k)])
|
for cur in range(0, len(s), 2 * k):
|
||||||
|
res[cur: cur + k] = reverse_substring(res[cur: cur + k])
|
||||||
# combine list into str.
|
|
||||||
return reduce(lambda a, b: a+b, s)
|
|
||||||
|
|
||||||
|
return ''.join(res)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -948,72 +948,91 @@ class MyLinkedList {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Swift
|
|
||||||
```Swift
|
Swift:
|
||||||
|
|
||||||
|
```swift
|
||||||
class MyLinkedList {
|
class MyLinkedList {
|
||||||
var size = 0
|
var dummyHead: ListNode<Int>?
|
||||||
let head: ListNode
|
var size: Int
|
||||||
|
|
||||||
/** Initialize your data structure here. */
|
|
||||||
init() {
|
init() {
|
||||||
head = ListNode(-1)
|
dummyHead = ListNode(0)
|
||||||
|
size = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
|
|
||||||
func get(_ index: Int) -> Int {
|
func get(_ index: Int) -> Int {
|
||||||
if size > 0 && index < size {
|
if index >= size || index < 0 {
|
||||||
var tempHead = head
|
|
||||||
for _ in 0..<index {
|
|
||||||
tempHead = tempHead.next!
|
|
||||||
}
|
|
||||||
let currentNode = tempHead.next!
|
|
||||||
return currentNode.val
|
|
||||||
} else {
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var curNode = dummyHead?.next
|
||||||
|
var curIndex = index
|
||||||
|
|
||||||
|
while curIndex > 0 {
|
||||||
|
curNode = curNode?.next
|
||||||
|
curIndex -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return curNode?.value ?? -1
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
|
|
||||||
func addAtHead(_ val: Int) {
|
func addAtHead(_ val: Int) {
|
||||||
addAtIndex(0, val)
|
let newHead = ListNode(val)
|
||||||
|
newHead.next = dummyHead?.next
|
||||||
|
dummyHead?.next = newHead
|
||||||
|
size += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Append a node of value val to the last element of the linked list. */
|
|
||||||
func addAtTail(_ val: Int) {
|
func addAtTail(_ val: Int) {
|
||||||
addAtIndex(size, val)
|
let newNode = ListNode(val)
|
||||||
|
var curNode = dummyHead
|
||||||
|
while curNode?.next != nil {
|
||||||
|
curNode = curNode?.next
|
||||||
|
}
|
||||||
|
|
||||||
|
curNode?.next = newNode
|
||||||
|
size += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
|
|
||||||
func addAtIndex(_ index: Int, _ val: Int) {
|
func addAtIndex(_ index: Int, _ val: Int) {
|
||||||
if index > size {
|
if index > size {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let idx = (index >= 0 ? index : 0)
|
|
||||||
var tempHead = head
|
let newNode = ListNode(val)
|
||||||
for _ in 0 ..< idx {
|
var curNode = dummyHead
|
||||||
tempHead = tempHead.next!
|
var curIndex = index
|
||||||
|
|
||||||
|
while curIndex > 0 {
|
||||||
|
curNode = curNode?.next
|
||||||
|
curIndex -= 1
|
||||||
}
|
}
|
||||||
let currentNode = tempHead.next
|
|
||||||
let newNode = ListNode(val, currentNode)
|
newNode.next = curNode?.next
|
||||||
tempHead.next = newNode
|
curNode?.next = newNode
|
||||||
size += 1
|
size += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete the index-th node in the linked list, if the index is valid. */
|
|
||||||
func deleteAtIndex(_ index: Int) {
|
func deleteAtIndex(_ index: Int) {
|
||||||
if size > 0 && index < size {
|
if index >= size || index < 0 {
|
||||||
var tempHead = head
|
return
|
||||||
for _ in 0 ..< index {
|
|
||||||
tempHead = tempHead.next!
|
|
||||||
}
|
|
||||||
tempHead.next = tempHead.next!.next
|
|
||||||
size -= 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var curNode = dummyHead
|
||||||
|
for _ in 0..<index {
|
||||||
|
curNode = curNode?.next
|
||||||
|
}
|
||||||
|
|
||||||
|
curNode?.next = curNode?.next?.next
|
||||||
|
size -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||||
|
@ -233,7 +233,41 @@ var commonChars = function (words) {
|
|||||||
return res
|
return res
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
GO
|
||||||
|
```golang
|
||||||
|
func commonChars(words []string) []string {
|
||||||
|
length:=len(words)
|
||||||
|
fre:=make([][]int,0)//统计每个字符串的词频
|
||||||
|
res:=make([]string,0)
|
||||||
|
//统计词频
|
||||||
|
for i:=0;i<length;i++{
|
||||||
|
var row [26]int//存放该字符串的词频
|
||||||
|
for j:=0;j<len(words[i]);j++{
|
||||||
|
row[words[i][j]-97]++
|
||||||
|
}
|
||||||
|
fre=append(fre,row[:])
|
||||||
|
}
|
||||||
|
//查找一列的最小值
|
||||||
|
for j:=0;j<len(fre[0]);j++{
|
||||||
|
pre:=fre[0][j]
|
||||||
|
for i:=0;i<len(fre);i++{
|
||||||
|
pre=min(pre,fre[i][j])
|
||||||
|
}
|
||||||
|
//将该字符添加到结果集(按照次数)
|
||||||
|
tmpString:=string(j+97)
|
||||||
|
for i:=0;i<pre;i++{
|
||||||
|
res=append(res,tmpString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
func min(a,b int)int{
|
||||||
|
if a>b{
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
```
|
||||||
-----------------------
|
-----------------------
|
||||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||||
|
@ -197,15 +197,38 @@ class Solution {
|
|||||||
|
|
||||||
Python:
|
Python:
|
||||||
```python3
|
```python3
|
||||||
|
# 方法一,使用栈,推荐!
|
||||||
class Solution:
|
class Solution:
|
||||||
def removeDuplicates(self, s: str) -> str:
|
def removeDuplicates(self, s: str) -> str:
|
||||||
t = list()
|
res = list()
|
||||||
for i in s:
|
for item in s:
|
||||||
if t and t[-1] == i:
|
if res and res[-1] == item:
|
||||||
t.pop(-1)
|
res.pop()
|
||||||
else:
|
else:
|
||||||
t.append(i)
|
res.append(item)
|
||||||
return "".join(t) # 字符串拼接
|
return "".join(res) # 字符串拼接
|
||||||
|
```
|
||||||
|
|
||||||
|
```python3
|
||||||
|
# 方法二,使用双指针模拟栈,如果不让用栈可以作为备选方法。
|
||||||
|
class Solution:
|
||||||
|
def removeDuplicates(self, s: str) -> str:
|
||||||
|
res = list(s)
|
||||||
|
slow = fast = 0
|
||||||
|
length = len(res)
|
||||||
|
|
||||||
|
while fast < length:
|
||||||
|
# 如果一样直接换,不一样会把后面的填在slow的位置
|
||||||
|
res[slow] = res[fast]
|
||||||
|
|
||||||
|
# 如果发现和前一个一样,就退一格指针
|
||||||
|
if slow > 0 and res[slow] == res[slow - 1]:
|
||||||
|
slow -= 1
|
||||||
|
else:
|
||||||
|
slow += 1
|
||||||
|
fast += 1
|
||||||
|
|
||||||
|
return ''.join(res[0: slow])
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
Go:
|
||||||
|
@ -202,45 +202,27 @@ func replaceSpace(s string) string {
|
|||||||
|
|
||||||
python:
|
python:
|
||||||
```python
|
```python
|
||||||
class Solution(object):
|
class Solution:
|
||||||
def replaceSpace(self, s):
|
def replaceSpace(self, s: str) -> str:
|
||||||
"""
|
counter = s.count(' ')
|
||||||
:type s: str
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
list_s = list(s)
|
|
||||||
|
|
||||||
# 记录原本字符串的长度
|
|
||||||
original_end = len(s)
|
|
||||||
|
|
||||||
# 将空格改成%20 使得字符串总长增长 2n,n为原本空格数量。
|
|
||||||
# 所以记录空格数量就可以得到目标字符串的长度
|
|
||||||
n_space = 0
|
|
||||||
for ss in s:
|
|
||||||
if ss == ' ':
|
|
||||||
n_space += 1
|
|
||||||
|
|
||||||
list_s += ['0'] * 2 * n_space
|
|
||||||
|
|
||||||
# 设置左右指针位置
|
|
||||||
left, right = original_end - 1, len(list_s) - 1
|
|
||||||
|
|
||||||
# 循环直至左指针越界
|
|
||||||
while left >= 0:
|
|
||||||
if list_s[left] == ' ':
|
|
||||||
list_s[right] = '0'
|
|
||||||
list_s[right - 1] = '2'
|
|
||||||
list_s[right - 2] = '%'
|
|
||||||
right -= 3
|
|
||||||
else:
|
|
||||||
list_s[right] = list_s[left]
|
|
||||||
right -= 1
|
|
||||||
|
|
||||||
left -= 1
|
|
||||||
|
|
||||||
# 将list变回str,输出
|
res = list(s)
|
||||||
s = ''.join(list_s)
|
# 每碰到一个空格就多拓展两个格子,1 + 2 = 3个位置存’%20‘
|
||||||
return s
|
res.extend([' '] * counter * 2)
|
||||||
|
|
||||||
|
# 原始字符串的末尾,拓展后的末尾
|
||||||
|
left, right = len(s) - 1, len(res) - 1
|
||||||
|
|
||||||
|
while left >= 0:
|
||||||
|
if res[left] != ' ':
|
||||||
|
res[right] = res[left]
|
||||||
|
right -= 1
|
||||||
|
else:
|
||||||
|
# [right - 2, right), 左闭右开
|
||||||
|
res[right - 2: right + 1] = '%20'
|
||||||
|
right -= 3
|
||||||
|
left -= 1
|
||||||
|
return ''.join(res)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -125,24 +125,45 @@ python:
|
|||||||
class Solution:
|
class Solution:
|
||||||
def reverseLeftWords(self, s: str, n: int) -> str:
|
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||||
return s[n:] + s[0:n]
|
return s[n:] + s[0:n]
|
||||||
|
```
|
||||||
|
```python
|
||||||
# 方法二:也可以使用上文描述的方法,有些面试中不允许使用切片,那就使用上文作者提到的方法
|
# 方法二:也可以使用上文描述的方法,有些面试中不允许使用切片,那就使用上文作者提到的方法
|
||||||
# class Solution:
|
class Solution:
|
||||||
# def reverseLeftWords(self, s: str, n: int) -> str:
|
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||||
# s = list(s)
|
s = list(s)
|
||||||
# s[0:n] = list(reversed(s[0:n]))
|
s[0:n] = list(reversed(s[0:n]))
|
||||||
# s[n:] = list(reversed(s[n:]))
|
s[n:] = list(reversed(s[n:]))
|
||||||
# s.reverse()
|
s.reverse()
|
||||||
|
|
||||||
|
return "".join(s)
|
||||||
|
|
||||||
# return "".join(s)
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 方法三:如果连reversed也不让使用,那么自己手写一个
|
||||||
|
class Solution:
|
||||||
|
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||||
|
def reverse_sub(lst, left, right):
|
||||||
|
while left < right:
|
||||||
|
lst[left], lst[right] = lst[right], lst[left]
|
||||||
|
left += 1
|
||||||
|
right -= 1
|
||||||
|
|
||||||
|
res = list(s)
|
||||||
|
end = len(res) - 1
|
||||||
|
reverse_sub(res, 0, n - 1)
|
||||||
|
reverse_sub(res, n, end)
|
||||||
|
reverse_sub(res, 0, end)
|
||||||
|
return ''.join(res)
|
||||||
|
|
||||||
|
# 同方法二
|
||||||
# 时间复杂度:O(n)
|
# 时间复杂度:O(n)
|
||||||
# 空间复杂度:O(n),python的string为不可变,需要开辟同样大小的list空间来修改
|
# 空间复杂度:O(n),python的string为不可变,需要开辟同样大小的list空间来修改
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python 3
|
```python 3
|
||||||
#方法三:考虑不能用切片的情况下,利用模+下标实现
|
#方法四:考虑不能用切片的情况下,利用模+下标实现
|
||||||
class Solution:
|
class Solution:
|
||||||
def reverseLeftWords(self, s: str, n: int) -> str:
|
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||||
new_s = ''
|
new_s = ''
|
||||||
|
Reference in New Issue
Block a user