mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-09 11:34:46 +08:00
20210824
This commit is contained in:
@ -9,7 +9,7 @@
|
||||
|
||||
## 1. 两数之和
|
||||
|
||||
https://leetcode-cn.com/problems/two-sum/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/two-sum/)
|
||||
|
||||
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
|
||||
|
||||
@ -29,10 +29,10 @@ https://leetcode-cn.com/problems/two-sum/
|
||||
很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。
|
||||
|
||||
建议大家做这道题目之前,先做一下这两道
|
||||
* [242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)
|
||||
* [349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)
|
||||
* [242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html)
|
||||
* [349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)
|
||||
|
||||
[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)这道题目是通过set作为哈希表来解决哈希问题。
|
||||
[242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)这道题目是通过set作为哈希表来解决哈希问题。
|
||||
|
||||
本题呢,则要使用map,那么来看一下使用数组和set来做哈希法的局限。
|
||||
|
||||
@ -51,7 +51,7 @@ C++中map,有三种类型:
|
||||
|
||||
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。
|
||||
|
||||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。
|
||||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://www.programmercarl.com/哈希表理论基础.html)。
|
||||
|
||||
**这道题目中并不需要key有序,选择std::unordered_map 效率更高!**
|
||||
|
||||
@ -110,13 +110,14 @@ Python:
|
||||
```python
|
||||
class Solution:
|
||||
def twoSum(self, nums: List[int], target: int) -> List[int]:
|
||||
hashmap={}
|
||||
for ind,num in enumerate(nums):
|
||||
hashmap[num] = ind
|
||||
for i,num in enumerate(nums):
|
||||
j = hashmap.get(target - num)
|
||||
if j is not None and i!=j:
|
||||
return [i,j]
|
||||
records = dict()
|
||||
|
||||
# 用枚举更方便,就不需要通过索引再去取当前位置的值
|
||||
for idx, val in enumerate(nums):
|
||||
if target - val not in records:
|
||||
records[val] = idx
|
||||
else:
|
||||
return [records[target - val], idx] # 如果存在就返回字典记录索引和当前索引
|
||||
```
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
# 5.最长回文子串
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/longest-palindromic-substring/)
|
||||
|
||||
给你一个字符串 s,找到 s 中最长的回文子串。
|
||||
|
||||
@ -30,11 +30,11 @@
|
||||
示例 4:
|
||||
* 输入:s = "ac"
|
||||
* 输出:"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题. 三数之和
|
||||
|
||||
https://leetcode-cn.com/problems/3sum/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/3sum/)
|
||||
|
||||
给你一个包含 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) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。
|
||||
|
||||
把符合条件的三元组放进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]。
|
||||
|
||||
接下来如何移动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相遇为止。
|
||||
|
||||
@ -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.电话号码的字母组合
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/)
|
||||
|
||||
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
如果输入"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循环的问题
|
||||
|
||||
对于回溯法还不了解的同学看这篇:[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)
|
||||
对于回溯法还不了解的同学看这篇:[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)
|
||||
|
||||
|
||||
例如:输入:"23",抽象为树形结构,如图所示:
|
||||
@ -75,7 +75,7 @@ const string letterMap[10] = {
|
||||
|
||||
再来看参数,参数指定是有题目中给的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也表示树的深度。
|
||||
|
||||
@ -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 * #按键等等异常情况
|
||||
@ -134,7 +134,7 @@ for (int i = 0; i < letters.size(); i++) {
|
||||
|
||||
## C++代码
|
||||
|
||||
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中的回溯法模板,不难写出如下C++代码:
|
||||
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中的回溯法模板,不难写出如下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题. 四数之和
|
||||
|
||||
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 相等?找出所有满足条件且不重复的四元组。
|
||||
|
||||
@ -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是任意值。(大家亲自写代码就能感受出来)
|
||||
|
||||
[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) 。
|
||||
|
||||
那么一样的道理,五数之和、六数之和等等都采用这种解法。
|
||||
|
||||
对于[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)的解法。也就是降一个数量级,题目如下:
|
||||
|
||||
* [27.移除元素](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww)
|
||||
* [15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)
|
||||
* [18.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
|
||||
* [27.移除元素](https://programmercarl.com/0027.移除元素.html)
|
||||
* [15.三数之和](https://programmercarl.com/0015.三数之和.html)
|
||||
* [18.四数之和](https://programmercarl.com/0018.四数之和.html)
|
||||
|
||||
|
||||
操作链表:
|
||||
|
||||
* [206.反转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)
|
||||
* [19.删除链表的倒数第N个节点](https://mp.weixin.qq.com/s/gxu65X1343xW_sBrkTz0Eg)
|
||||
* [面试题 02.07. 链表相交](https://mp.weixin.qq.com/s/BhfFfaGvt9Zs7UmH4YehZw)
|
||||
* [142题.环形链表II](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ)
|
||||
* [206.反转链表](https://programmercarl.com/0206.翻转链表.html)
|
||||
* [19.删除链表的倒数第N个节点](https://programmercarl.com/0019.删除链表的倒数第N个节点.html)
|
||||
* [面试题 02.07. 链表相交](https://programmercarl.com/面试题02.07.链表相交.html)
|
||||
* [142题.环形链表II](https://programmercarl.com/0142.环形链表II.html)
|
||||
|
||||
双指针法在字符串题目中还有很多应用,后面还会介绍到。
|
||||
|
||||
@ -167,7 +167,33 @@ class Solution {
|
||||
|
||||
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):
|
||||
def fourSum(self, nums, target):
|
||||
"""
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
## 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 个结点,并且返回链表的头结点。
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
分为如下几步:
|
||||
|
||||
* 首先这里我推荐大家使用虚拟头结点,这样方面处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://mp.weixin.qq.com/s/L5aanfALdLEwVWGvyXPDqA)
|
||||
* 首先这里我推荐大家使用虚拟头结点,这样方面处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)
|
||||
|
||||
* 定义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)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
# 20. 有效的括号
|
||||
|
||||
https://leetcode-cn.com/problems/valid-parentheses/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/valid-parentheses/)
|
||||
|
||||
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
|
||||
|
||||
@ -162,18 +162,44 @@ class Solution {
|
||||
|
||||
Python:
|
||||
```python3
|
||||
# 方法一,仅使用栈,更省空间
|
||||
class Solution:
|
||||
def isValid(self, s: str) -> bool:
|
||||
stack = [] # 保存还未匹配的左括号
|
||||
mapping = {")": "(", "]": "[", "}": "{"}
|
||||
for i in s:
|
||||
if i in "([{": # 当前是左括号,则入栈
|
||||
stack.append(i)
|
||||
elif stack and stack[-1] == mapping[i]: # 当前是配对的右括号则出栈
|
||||
stack.pop()
|
||||
else: # 不是匹配的右括号或者没有左括号与之匹配,则返回false
|
||||
stack = []
|
||||
|
||||
for item in s:
|
||||
if item == '(':
|
||||
stack.append(')')
|
||||
elif item == '[':
|
||||
stack.append(']')
|
||||
elif item == '{':
|
||||
stack.append('}')
|
||||
elif not stack or stack[-1] != item:
|
||||
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:
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
## 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
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
|
||||
class Solution:
|
||||
def swapPairs(self, head: ListNode) -> ListNode:
|
||||
dummy = ListNode(0) #设置一个虚拟头结点
|
||||
dummy.next = head
|
||||
cur = dummy
|
||||
while cur.next and cur.next.next:
|
||||
tmp = cur.next #记录临时节点
|
||||
tmp1 = cur.next.next.next #记录临时节点
|
||||
res = ListNode(next=head)
|
||||
pre = res
|
||||
|
||||
# 必须有pre的下一个和下下个才能交换,否则说明已经交换结束了
|
||||
while pre.next and pre.next.next:
|
||||
cur = pre.next
|
||||
post = pre.next.next
|
||||
|
||||
cur.next = cur.next.next #步骤一
|
||||
cur.next.next = tmp #步骤二
|
||||
cur.next.next.next = tmp1 #步骤三
|
||||
|
||||
cur = cur.next.next #cur移动两位,准备下一轮交换
|
||||
return dummy.next
|
||||
# pre,cur,post对应最左,中间的,最右边的节点
|
||||
cur.next = post.next
|
||||
post.next = cur
|
||||
pre.next = post
|
||||
|
||||
pre = pre.next.next
|
||||
return res.next
|
||||
```
|
||||
|
||||
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. 移除元素
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/remove-element/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/remove-element/)
|
||||
|
||||
给你一个数组 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(1)$
|
||||
|
||||
旧文链接:[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
|
||||
旧文链接:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)
|
||||
|
||||
## 相关题目推荐
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
# 28. 实现 strStr()
|
||||
|
||||
https://leetcode-cn.com/problems/implement-strstr/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/implement-strstr/)
|
||||
|
||||
实现 strStr() 函数。
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
# 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)
|
||||
* [35.搜索插入位置](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)
|
||||
* [704.二分查找](https://programmercarl.com/0704.二分查找.html)
|
||||
* [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的右边界,左边界同理。
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
# 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(1)
|
||||
空间复杂度:O(1)
|
||||
|
||||
效率如下:
|
||||
|
||||
@ -238,16 +238,16 @@ Python:
|
||||
```python3
|
||||
class Solution:
|
||||
def searchInsert(self, nums: List[int], target: int) -> int:
|
||||
left, right = 0, len(nums) - 1
|
||||
|
||||
left, right = 0, len(nums) - 1
|
||||
|
||||
while left <= right:
|
||||
middle = (left + right) // 2
|
||||
|
||||
|
||||
if nums[middle] < target:
|
||||
left = middle + 1
|
||||
elif nums[middle] > target:
|
||||
right = middle - 1
|
||||
else:
|
||||
else:
|
||||
return middle
|
||||
return right + 1
|
||||
```
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
## 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皇后更宽更深**。
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
**递归函数的返回值需要是bool类型,为什么呢?**
|
||||
|
||||
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,这一点在[回溯算法:N皇后问题](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg)中已经介绍过了,一样的道理。
|
||||
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,这一点在[回溯算法:N皇后问题](https://programmercarl.com/0051.N皇后.html)中已经介绍过了,一样的道理。
|
||||
|
||||
代码如下:
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
## 39. 组合总和
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/combination-sum/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/combination-sum/)
|
||||
|
||||
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
||||
|
||||
@ -44,14 +44,14 @@ candidates 中的数字可以无限制重复被选取。
|
||||
|
||||
题目中的**无限制重复被选取,吓得我赶紧想想 出现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,就返回!
|
||||
|
||||
而在[回溯算法:求组合问题!](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,例如:[回溯算法:求组合问题!](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集合。
|
||||
|
||||
**注意本题和[回溯算法:求组合问题!](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
|
||||
// 版本一
|
||||
@ -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
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/combination-sum-ii/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/combination-sum-ii/)
|
||||
|
||||
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
||||
|
||||
@ -44,12 +44,12 @@ candidates 中的每个数字在每个组合中只能使用一次。
|
||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
||||
|
||||
|
||||
这道题目和[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)如下区别:
|
||||
这道题目和[39.组合总和](https://programmercarl.com/0039.组合总和.html)如下区别:
|
||||
|
||||
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)有重复元素,但还不能有重复的组合**。
|
||||
|
||||
@ -84,7 +84,7 @@ candidates 中的每个数字在每个组合中只能使用一次。
|
||||
|
||||
* **递归函数参数**
|
||||
|
||||
与[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
|
||||
与[39.组合总和](https://programmercarl.com/0039.组合总和.html)套路相同,此题还需要加一个bool型数组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)难度提升了不少。
|
||||
|
||||
**关键是去重的逻辑,代码很简单,网上一搜一大把,但几乎没有能把这块代码含义讲明白的,基本都是给出代码,然后说这就是去重了,究竟怎么个去重法也是模棱两可**。
|
||||
|
||||
@ -296,7 +296,7 @@ class Solution {
|
||||
}
|
||||
```
|
||||
Python:
|
||||
```py
|
||||
```python
|
||||
class Solution:
|
||||
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
|
||||
res = []
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
# 42. 接雨水
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/trapping-rain-water/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/trapping-rain-water/)
|
||||
|
||||
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
* 输入:height = [4,2,0,3,2,5]
|
||||
* 输出:9
|
||||
|
||||
|
||||
|
||||
# 思路
|
||||
|
||||
@ -186,7 +186,7 @@ public:
|
||||
|
||||
这个解法可以说是最不好理解的了,所以下面我花了大量的篇幅来介绍这种方法。
|
||||
|
||||
单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://mp.weixin.qq.com/s/Xgcqx5eBa3xZabt_LurnNQ)一样,需要我们自己维持顺序,没有现成的容器可以用。
|
||||
单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://programmercarl.com/0239.滑动窗口最大值.html)一样,需要我们自己维持顺序,没有现成的容器可以用。
|
||||
|
||||
|
||||
### 准备工作
|
||||
@ -366,6 +366,58 @@ public:
|
||||
|
||||
Java:
|
||||
|
||||
双指针法
|
||||
```java
|
||||
class Solution {
|
||||
public int trap(int[] height) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < height.length; i++) {
|
||||
// 第一个柱子和最后一个柱子不接雨水
|
||||
if (i==0 || i== height.length - 1) continue;
|
||||
|
||||
int rHeight = height[i]; // 记录右边柱子的最高高度
|
||||
int lHeight = height[i]; // 记录左边柱子的最高高度
|
||||
for (int r = i+1; r < height.length; r++) {
|
||||
if (height[r] > rHeight) rHeight = height[r];
|
||||
}
|
||||
for (int l = i-1; l >= 0; l--) {
|
||||
if(height[l] > lHeight) lHeight = height[l];
|
||||
}
|
||||
int h = Math.min(lHeight, rHeight) - height[i];
|
||||
if (h > 0) sum += h;
|
||||
}
|
||||
return sum;
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
动态规划法
|
||||
```java
|
||||
class Solution {
|
||||
public int trap(int[] height) {
|
||||
int length = height.length;
|
||||
if (length <= 2) return 0;
|
||||
int[] maxLeft = new int[length];
|
||||
int[] maxRight = new int[length];
|
||||
|
||||
// 记录每个柱子左边柱子最大高度
|
||||
maxLeft[0] = height[0];
|
||||
for (int i = 1; i< length; i++) maxLeft[i] = Math.max(height[i], maxLeft[i-1]);
|
||||
|
||||
// 记录每个柱子右边柱子最大高度
|
||||
maxRight[length - 1] = height[length - 1];
|
||||
for(int i = length - 2; i >= 0; i--) maxRight[i] = Math.max(height[i], maxRight[i+1]);
|
||||
|
||||
// 求和
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
int count = Math.min(maxLeft[i], maxRight[i]) - height[i];
|
||||
if (count > 0) sum += count;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
```
|
||||
Python:
|
||||
|
||||
双指针法
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
## 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.全排列
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/permutations/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/permutations/)
|
||||
|
||||
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
|
||||
|
||||
@ -30,11 +30,11 @@
|
||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](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循环暴力把结果搜索出来,这个暴力也不是很好写。
|
||||
|
||||
所以正如我们在[关于回溯算法,你该了解这些!](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。
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
## 47.全排列 II
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/permutations-ii/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/permutations-ii/)
|
||||
|
||||
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
|
||||
|
||||
@ -33,11 +33,11 @@
|
||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](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++代码
|
||||
|
||||
|
@ -302,6 +302,61 @@ func generateMatrix(n int) [][]int {
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func generateMatrix(_ n: Int) -> [[Int]] {
|
||||
var result = [[Int]](repeating: [Int](repeating: 0, count: n), count: n)
|
||||
|
||||
var startRow = 0
|
||||
var startColumn = 0
|
||||
var loopCount = n / 2
|
||||
let mid = n / 2
|
||||
var count = 1
|
||||
var offset = 1
|
||||
var row: Int
|
||||
var column: Int
|
||||
|
||||
while loopCount > 0 {
|
||||
row = startRow
|
||||
column = startColumn
|
||||
|
||||
for c in column ..< startColumn + n - offset {
|
||||
result[startRow][c] = count
|
||||
count += 1
|
||||
column += 1
|
||||
}
|
||||
|
||||
for r in row ..< startRow + n - offset {
|
||||
result[r][column] = count
|
||||
count += 1
|
||||
row += 1
|
||||
}
|
||||
|
||||
for _ in startColumn ..< column {
|
||||
result[row][column] = count
|
||||
count += 1
|
||||
column -= 1
|
||||
}
|
||||
|
||||
for _ in startRow ..< row {
|
||||
result[row][column] = count
|
||||
count += 1
|
||||
row -= 1
|
||||
}
|
||||
|
||||
startRow += 1
|
||||
startColumn += 1
|
||||
offset += 2
|
||||
loopCount -= 1
|
||||
}
|
||||
|
||||
if (n % 2) != 0 {
|
||||
result[mid][mid] = count
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -88,7 +88,8 @@
|
||||
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
```
|
||||
|
||||
```cpp
|
||||
class Solution {
|
||||
public:
|
||||
int climbStairs(int n) {
|
||||
|
@ -93,10 +93,10 @@ class Solution:
|
||||
if not root:
|
||||
return []
|
||||
|
||||
quene = [root]
|
||||
queue = [root]
|
||||
out_list = []
|
||||
|
||||
while quene:
|
||||
while queue:
|
||||
length = len(queue)
|
||||
in_list = []
|
||||
for _ in range(length):
|
||||
@ -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
|
||||
@ -1129,7 +1155,7 @@ var largestValues = function(root) {
|
||||
queue.push(root);
|
||||
while(root!==null&&queue.length){
|
||||
//设置max初始值就是队列的第一个元素
|
||||
let max=queue[0];
|
||||
let max=queue[0].val;
|
||||
let length=queue.length;
|
||||
while(length--){
|
||||
let node = queue.shift();
|
||||
@ -1532,6 +1558,29 @@ Java:
|
||||
|
||||
|
||||
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:
|
||||
|
||||
@ -1539,6 +1588,8 @@ JavaScript:
|
||||
|
||||
# 111.二叉树的最小深度
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
|
||||
|
||||
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
|
||||
|
||||
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
|
||||
@ -1572,7 +1623,15 @@ public:
|
||||
```
|
||||
|
||||
Java:
|
||||
```java
|
||||
class Solution {
|
||||
|
||||
public int minDepth(TreeNode root){
|
||||
if (root == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
```java
|
||||
class Solution {
|
||||
public int minDepth(TreeNode root){
|
||||
@ -1602,8 +1661,68 @@ class Solution {
|
||||
```
|
||||
|
||||
|
||||
=======
|
||||
Queue<TreeNode> queue = new LinkedList<>();
|
||||
queue.offer(root);
|
||||
int depth = 0;
|
||||
>>>>>>> cf6edd67cd707e5cd33b8c9f774cfdd6a0227320
|
||||
|
||||
Python:
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
|
@ -165,7 +165,32 @@ public:
|
||||
Java:
|
||||
|
||||
Python:
|
||||
```python3
|
||||
class Solution:
|
||||
def sumNumbers(self, root: TreeNode) -> int:
|
||||
res = 0
|
||||
path = []
|
||||
def backtrace(root):
|
||||
nonlocal res
|
||||
if not root: return # 节点空则返回
|
||||
path.append(root.val)
|
||||
if not root.left and not root.right: # 遇到了叶子节点
|
||||
res += get_sum(path)
|
||||
if root.left: # 左子树不空
|
||||
backtrace(root.left)
|
||||
if root.right: # 右子树不空
|
||||
backtrace(root.right)
|
||||
path.pop()
|
||||
|
||||
def get_sum(arr):
|
||||
s = 0
|
||||
for i in range(len(arr)):
|
||||
s = s * 10 + arr[i]
|
||||
return s
|
||||
|
||||
backtrace(root)
|
||||
return res
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
|
@ -222,7 +222,61 @@ public class ReorderList {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
# 方法二 双向队列
|
||||
class Solution:
|
||||
def reorderList(self, head: ListNode) -> None:
|
||||
"""
|
||||
Do not return anything, modify head in-place instead.
|
||||
"""
|
||||
d = collections.deque()
|
||||
tmp = head
|
||||
while tmp.next: # 链表除了首元素全部加入双向队列
|
||||
d.append(tmp.next)
|
||||
tmp = tmp.next
|
||||
tmp = head
|
||||
while len(d): # 一后一前加入链表
|
||||
tmp.next = d.pop()
|
||||
tmp = tmp.next
|
||||
if len(d):
|
||||
tmp.next = d.popleft()
|
||||
tmp = tmp.next
|
||||
tmp.next = None # 尾部置空
|
||||
|
||||
# 方法三 反转链表
|
||||
class Solution:
|
||||
def reorderList(self, head: ListNode) -> None:
|
||||
if head == None or head.next == None:
|
||||
return True
|
||||
slow, fast = head, head
|
||||
while fast and fast.next:
|
||||
slow = slow.next
|
||||
fast = fast.next.next
|
||||
right = slow.next # 分割右半边
|
||||
slow.next = None # 切断
|
||||
right = self.reverseList(right) #反转右半边
|
||||
left = head
|
||||
# 左半边一定比右半边长, 因此判断右半边即可
|
||||
while right:
|
||||
curLeft = left.next
|
||||
left.next = right
|
||||
left = curLeft
|
||||
|
||||
curRight = right.next
|
||||
right.next = left
|
||||
right = curRight
|
||||
|
||||
|
||||
def reverseList(self, head: ListNode) -> ListNode:
|
||||
cur = head
|
||||
pre = None
|
||||
while(cur!=None):
|
||||
temp = cur.next # 保存一下cur的下一个节点
|
||||
cur.next = pre # 反转
|
||||
pre = cur
|
||||
cur = temp
|
||||
return pre
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
|
@ -223,17 +223,19 @@ var evalRPN = function(tokens) {
|
||||
python3
|
||||
|
||||
```python
|
||||
def evalRPN(tokens) -> int:
|
||||
stack = list()
|
||||
for i in range(len(tokens)):
|
||||
if tokens[i] not in ["+", "-", "*", "/"]:
|
||||
stack.append(tokens[i])
|
||||
else:
|
||||
tmp1 = stack.pop()
|
||||
tmp2 = stack.pop()
|
||||
res = eval(tmp2+tokens[i]+tmp1)
|
||||
stack.append(str(int(res)))
|
||||
return stack[-1]
|
||||
class Solution:
|
||||
def evalRPN(self, tokens: List[str]) -> int:
|
||||
stack = []
|
||||
for item in tokens:
|
||||
if item not in {"+", "-", "*", "/"}:
|
||||
stack.append(item)
|
||||
else:
|
||||
first_num, second_num = stack.pop(), stack.pop()
|
||||
stack.append(
|
||||
int(eval(f'{second_num} {item} {first_num}')) # 第一个出来的在运算符后面
|
||||
)
|
||||
return int(stack.pop()) # 如果一开始只有一个数,那么会是字符串形式的
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
@ -196,7 +196,7 @@ class Solution {
|
||||
}
|
||||
}
|
||||
|
||||
// 版本二: 空间优化
|
||||
// 版本二: 二维 dp数组
|
||||
class Solution {
|
||||
public int maxProfit(int k, int[] prices) {
|
||||
if (prices.length == 0) return 0;
|
||||
@ -220,6 +220,25 @@ class Solution {
|
||||
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];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -111,25 +111,29 @@ Python:
|
||||
```python
|
||||
class Solution:
|
||||
def isHappy(self, n: int) -> bool:
|
||||
set_ = set()
|
||||
while 1:
|
||||
sum_ = self.getSum(n)
|
||||
if sum_ == 1:
|
||||
def calculate_happy(num):
|
||||
sum_ = 0
|
||||
|
||||
# 从个位开始依次取,平方求和
|
||||
while num:
|
||||
sum_ += (num % 10) ** 2
|
||||
num = num // 10
|
||||
return sum_
|
||||
|
||||
# 记录中间结果
|
||||
record = set()
|
||||
|
||||
while True:
|
||||
n = calculate_happy(n)
|
||||
if n == 1:
|
||||
return True
|
||||
#如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
|
||||
if sum_ in set_:
|
||||
|
||||
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
|
||||
if n in record:
|
||||
return False
|
||||
else:
|
||||
set_.add(sum_)
|
||||
n = sum_
|
||||
|
||||
#取数值各个位上的单数之和
|
||||
def getSum(self, n):
|
||||
sum_ = 0
|
||||
while n > 0:
|
||||
sum_ += (n%10) * (n%10)
|
||||
n //= 10
|
||||
return sum_
|
||||
record.add(n)
|
||||
|
||||
```
|
||||
|
||||
Go:
|
||||
|
@ -245,13 +245,15 @@ Python:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
|
||||
class Solution:
|
||||
def removeElements(self, head: ListNode, val: int) -> ListNode:
|
||||
dummy_head = ListNode(next=head) #添加一个虚拟节点
|
||||
dummy_head = ListNode(next=head)
|
||||
cur = dummy_head
|
||||
while(cur.next!=None):
|
||||
if(cur.next.val == val):
|
||||
cur.next = cur.next.next #删除cur.next节点
|
||||
|
||||
while cur.next:
|
||||
if cur.next.val == val:
|
||||
cur.next = cur.next.next # 删除下一个节点
|
||||
else:
|
||||
cur = cur.next
|
||||
return dummy_head.next
|
||||
@ -304,6 +306,33 @@ var removeElements = function(head, val) {
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
```swift
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* public class ListNode {
|
||||
* public var val: Int
|
||||
* public var next: ListNode?
|
||||
* public init() { self.val = 0; self.next = nil; }
|
||||
* public init(_ val: Int) { self.val = val; self.next = nil; }
|
||||
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
|
||||
* }
|
||||
*/
|
||||
func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? {
|
||||
let dummyNode = ListNode()
|
||||
dummyNode.next = head
|
||||
var currentNode = dummyNode
|
||||
while let curNext = currentNode.next {
|
||||
if curNext.val == val {
|
||||
currentNode.next = curNext.next
|
||||
} else {
|
||||
currentNode = curNext
|
||||
}
|
||||
}
|
||||
return dummyNode.next
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -334,6 +334,43 @@ fun reverseList(head: ListNode?): ListNode? {
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
```swift
|
||||
/// 双指针法 (迭代)
|
||||
/// - Parameter head: 头结点
|
||||
/// - Returns: 翻转后的链表头结点
|
||||
func reverseList(_ head: ListNode?) -> ListNode? {
|
||||
if head == nil || head?.next == nil {
|
||||
return head
|
||||
}
|
||||
var pre: ListNode? = nil
|
||||
var cur: ListNode? = head
|
||||
var temp: ListNode? = nil
|
||||
while cur != nil {
|
||||
temp = cur?.next
|
||||
cur?.next = pre
|
||||
pre = cur
|
||||
cur = temp
|
||||
}
|
||||
return pre
|
||||
}
|
||||
|
||||
/// 递归
|
||||
/// - Parameter head: 头结点
|
||||
/// - Returns: 翻转后的链表头结点
|
||||
func reverseList2(_ head: ListNode?) -> ListNode? {
|
||||
return reverse(pre: nil, cur: head)
|
||||
}
|
||||
func reverse(pre: ListNode?, cur: ListNode?) -> ListNode? {
|
||||
if cur == nil {
|
||||
return pre
|
||||
}
|
||||
let temp: ListNode? = cur?.next
|
||||
cur?.next = pre
|
||||
return reverse(pre: cur, cur: temp)
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -294,53 +294,66 @@ Python:
|
||||
|
||||
```python
|
||||
from collections import deque
|
||||
|
||||
class MyStack:
|
||||
|
||||
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.que1 = deque()
|
||||
self.que2 = deque()
|
||||
self.queue_in = deque()
|
||||
self.queue_out = deque()
|
||||
|
||||
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:
|
||||
"""
|
||||
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)
|
||||
size -= 1#这里先减一是为了保证最后面的元素
|
||||
while size > 0:
|
||||
size -= 1
|
||||
self.que2.append(self.que1.popleft())
|
||||
if self.empty():
|
||||
return None
|
||||
|
||||
|
||||
result = self.que1.popleft()
|
||||
self.que1, self.que2= self.que2, self.que1#将que2和que1交换 que1经过之前的操作应该是空了
|
||||
#一定注意不能直接使用que1 = que2 这样que2的改变会影响que1 可以用浅拷贝
|
||||
return result
|
||||
for i in range(len(self.queue_in) - 1):
|
||||
self.queue_out.append(self.queue_in.popleft())
|
||||
|
||||
self.queue_in, self.queue_out = self.queue_out, self.queue_in # 交换in和out,这也是为啥in只用来存
|
||||
return self.queue_out.popleft()
|
||||
|
||||
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:
|
||||
"""
|
||||
Returns whether the stack is empty.
|
||||
因为只有in存了数据,只要判断in是不是有数即可
|
||||
"""
|
||||
#print(self.que1)
|
||||
if len(self.que1) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return len(self.queue_in) == 0
|
||||
|
||||
```
|
||||
|
||||
|
@ -234,48 +234,60 @@ class MyQueue {
|
||||
|
||||
Python:
|
||||
```python
|
||||
# 使用两个栈实现先进先出的队列
|
||||
class MyQueue:
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize your data structure here.
|
||||
in主要负责push,out主要负责pop
|
||||
"""
|
||||
self.stack1 = list()
|
||||
self.stack2 = list()
|
||||
self.stack_in = []
|
||||
self.stack_out = []
|
||||
|
||||
|
||||
def push(self, x: int) -> None:
|
||||
"""
|
||||
Push element x to the back of queue.
|
||||
有新元素进来,就往in里面push
|
||||
"""
|
||||
# self.stack1用于接受元素
|
||||
self.stack1.append(x)
|
||||
self.stack_in.append(x)
|
||||
|
||||
|
||||
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.stack2 == []:
|
||||
while self.stack1:
|
||||
tmp = self.stack1.pop()
|
||||
self.stack2.append(tmp)
|
||||
return self.stack2.pop()
|
||||
if self.empty:
|
||||
return None
|
||||
|
||||
if self.stack_out:
|
||||
return self.stack_out.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:
|
||||
"""
|
||||
Get the front element.
|
||||
1. 查out有没有元素,有就把最上面的返回
|
||||
2. 如果out没有元素,就把in最下面的返回
|
||||
"""
|
||||
if self.stack2 == []:
|
||||
while self.stack1:
|
||||
tmp = self.stack1.pop()
|
||||
self.stack2.append(tmp)
|
||||
return self.stack2[-1]
|
||||
if self.empty:
|
||||
return None
|
||||
|
||||
if self.stack_out:
|
||||
return self.stack_out[-1]
|
||||
else:
|
||||
return self.stack_in[0]
|
||||
|
||||
|
||||
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)
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
@ -148,7 +148,62 @@ public:
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
```python3
|
||||
#数组模拟
|
||||
class Solution:
|
||||
def isPalindrome(self, head: ListNode) -> bool:
|
||||
length = 0
|
||||
tmp = head
|
||||
while tmp: #求链表长度
|
||||
length += 1
|
||||
tmp = tmp.next
|
||||
|
||||
result = [0] * length
|
||||
tmp = head
|
||||
index = 0
|
||||
while tmp: #链表元素加入数组
|
||||
result[index] = tmp.val
|
||||
index += 1
|
||||
tmp = tmp.next
|
||||
|
||||
i, j = 0, length - 1
|
||||
while i < j: # 判断回文
|
||||
if result[i] != result[j]:
|
||||
return False
|
||||
i += 1
|
||||
j -= 1
|
||||
return True
|
||||
|
||||
#反转后半部分链表
|
||||
class Solution:
|
||||
def isPalindrome(self, head: ListNode) -> bool:
|
||||
if head == None or head.next == None:
|
||||
return True
|
||||
slow, fast = head, head
|
||||
while fast and fast.next:
|
||||
pre = slow
|
||||
slow = slow.next
|
||||
fast = fast.next.next
|
||||
|
||||
pre.next = None # 分割链表
|
||||
cur1 = head # 前半部分
|
||||
cur2 = self.reverseList(slow) # 反转后半部分,总链表长度如果是奇数,cur2比cur1多一个节点
|
||||
while cur1:
|
||||
if cur1.val != cur2.val:
|
||||
return False
|
||||
cur1 = cur1.next
|
||||
cur2 = cur2.next
|
||||
return True
|
||||
|
||||
def reverseList(self, head: ListNode) -> ListNode:
|
||||
cur = head
|
||||
pre = None
|
||||
while(cur!=None):
|
||||
temp = cur.next # 保存一下cur的下一个节点
|
||||
cur.next = pre # 反转
|
||||
pre = cur
|
||||
cur = temp
|
||||
return pre
|
||||
```
|
||||
|
||||
## Go
|
||||
|
@ -161,6 +161,7 @@ public:
|
||||
Java:
|
||||
```Java
|
||||
class Solution {
|
||||
// 版本一,先遍历物品, 再遍历背包
|
||||
public int numSquares(int n) {
|
||||
int max = Integer.MAX_VALUE;
|
||||
int[] dp = new int[n + 1];
|
||||
@ -170,7 +171,9 @@ class Solution {
|
||||
}
|
||||
//当和为0时,组合的个数为0
|
||||
dp[0] = 0;
|
||||
// 遍历物品
|
||||
for (int i = 1; i * i <= n; i++) {
|
||||
// 遍历背包
|
||||
for (int j = i * i; j <= n; j++) {
|
||||
if (dp[j - i * i] != max) {
|
||||
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
|
||||
@ -180,6 +183,28 @@ class Solution {
|
||||
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:
|
||||
@ -187,7 +212,7 @@ Python:
|
||||
```python3
|
||||
class Solution:
|
||||
def numSquares(self, n: int) -> int:
|
||||
'''版本一'''
|
||||
'''版本一,先遍历背包, 再遍历物品'''
|
||||
# 初始化
|
||||
nums = [i**2 for i in range(1, n + 1) if i**2 <= n]
|
||||
dp = [10**4]*(n + 1)
|
||||
@ -201,7 +226,7 @@ class Solution:
|
||||
return dp[n]
|
||||
|
||||
def numSquares1(self, n: int) -> int:
|
||||
'''版本二'''
|
||||
'''版本二, 先遍历物品, 再遍历背包'''
|
||||
# 初始化
|
||||
nums = [i**2 for i in range(1, n + 1) if i**2 <= n]
|
||||
dp = [10**4]*(n + 1)
|
||||
@ -217,6 +242,22 @@ class Solution:
|
||||
Python3:
|
||||
```python
|
||||
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:
|
||||
# 初始化
|
||||
# 组成和的完全平方数的最多个数,就是只用1构成
|
||||
|
@ -33,7 +33,7 @@
|
||||
* 1 <= nums.length <= 2500
|
||||
* -10^4 <= nums[i] <= 104
|
||||
|
||||
|
||||
## 方法一 动态规划
|
||||
## 思路
|
||||
|
||||
最长上升子序列是动规的经典题目,这里dp[i]是可以根据dp[j] (j < i)推导出来的,那么依然用动规五部曲来分析详细一波:
|
||||
@ -190,10 +190,130 @@ const lengthOfLIS = (nums) => {
|
||||
};
|
||||
```
|
||||
*复杂度分析*
|
||||
- 时间复杂度:O(nlogn)。数组 nums 的长度为 n,我们依次用数组中的元素去更新 dp 数组,相当于插入最后递增的元素,而更新 dp 数组时需要进行 O(logn) 的二分搜索,所以总时间复杂度为 O(nlogn)。
|
||||
- 时间复杂度:O(n^2)。数组 nums 的长度为 n,我们依次用数组中的元素去遍历 dp 数组,而遍历 dp 数组时需要进行 O(n) 次搜索,所以总时间复杂度为 O(n^2)。
|
||||
- 空间复杂度:O(n),需要额外使用长度为 n 的 dp 数组。
|
||||
|
||||
|
||||
## 方法二 贪心策略+二分搜索
|
||||
|
||||
使用贪心策略和二分搜索可以进一步将算法时间复杂度将为O(nlogn)。
|
||||
|
||||
## 思路
|
||||
|
||||
为了使得到的子序列尽可能长,我们需要使序列上升得尽可能慢。
|
||||
|
||||
对于长度为n的数组 nums,我们从0到n-1依次遍历数组中的每个元素nums[i],更新在0到i范围内最长上升子序列的长度len,以及 在0到i范围内,上升子序列的长度为1到len时,对应长度子序列最右端的最小值,将结果保存在list中。实际编码过程中,list长度即为len。
|
||||
|
||||
## 可行性
|
||||
当我们遍历完数组nums中第n-1个元素时,list中保存的是0到n-1范围内最长上升子序列的长度,即为所求。
|
||||
|
||||
## 算法复杂度分析
|
||||
1. list中的元素是单调递增的。可以用反证法来证明:假设对于0<=i<j<len,有list[i]>=list[j],那么我们可以在list[j]对应的子序列中删除最后j-i个元素得到长度与list[i]相同的子序列,其最右端的值max<list[j]<=list[i],与list的定义矛盾。
|
||||
|
||||
2. 假设我们已经得到0到i-1范围内对应的list,我们可以在O(logn)的时间复杂度内更新list,得到0到i范围内的list。
|
||||
|
||||
1. if(nums[i]>list[len-1],此时,list中子序列长度为1到len的对应的最右端最小值不变,并新增长度为len+1的子序列,最右端的最小值为nums[i],时间复杂度O(1);
|
||||
|
||||
2. if(nums[i]<=list[len-1]),此时,我们可以在0到len-1范围内找到k,list[k]为>=nums[i]的最小值,由于list单调递增,所以我们可以使用二分搜索在O(logn)的时间复杂度内找到k。
|
||||
1. 对于0<=j<k,list[j]<nums[i]恒成立,对应list[j]的值不需要更新。
|
||||
2. 对于list[k],其值更新为nums[i],因为原本list[k]对应的子序列的倒数第二项的值可以=list[k-1]<nums[i]。
|
||||
3. 对于k<j<=len-1,对应的list[j]不需要更新。因为这些list[j]对应的子序列的倒数第二项的值>nums[i];
|
||||
|
||||
3. 综上,算法时间复杂度为O(nlogn),空间复杂度为O(n),需要O(n)的空间保存list。
|
||||
|
||||
代码如下
|
||||
|
||||
Java
|
||||
```java
|
||||
class Solution {
|
||||
public int lengthOfLIS(int[] nums) {
|
||||
int n = nums.length;
|
||||
if(n==0){return 0;}
|
||||
|
||||
List<Integer> list=new ArrayList<>();
|
||||
list.add(nums[0]);
|
||||
for (int i = 1; i < n; ++i) {
|
||||
if (nums[i] > list.get(list.size()-1)) {
|
||||
list.add(nums[i]);
|
||||
} else {
|
||||
int k=binarySearch(list,nums[i]);
|
||||
list.set(k,nums[i]);
|
||||
}
|
||||
}
|
||||
return list.size();
|
||||
}
|
||||
|
||||
int binarySearch(List<Integer>list, int num){
|
||||
int len=list.size();
|
||||
int l=0,r=len-1,ans=len-1;
|
||||
while(l<=r){
|
||||
int mid=l+(r-l)/2;
|
||||
if(list.get(mid)<num){
|
||||
l=mid+1;
|
||||
}else{
|
||||
r=mid-1;
|
||||
ans=mid;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实际运行过程中,list的长度不会超过n,所以我们可以用数组来模拟list,代码如下。
|
||||
|
||||
|
||||
Java
|
||||
```java
|
||||
class Solution {
|
||||
public int lengthOfLIS(int[] nums) {
|
||||
int n = nums.length;
|
||||
if(n==0){return 0;}
|
||||
|
||||
//初始化list,len记录list长度
|
||||
int[] list=new int[n];
|
||||
int len=0;
|
||||
|
||||
//添加元素到list并更新len的值
|
||||
list[len++]=nums[0];
|
||||
|
||||
for (int i = 1; i < n; ++i) {
|
||||
if (nums[i] > list[len-1]) {
|
||||
list[len++]=nums[i];
|
||||
} else {
|
||||
int k=binarySearch(list,len,nums[i]);
|
||||
list[k]=nums[i];
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int binarySearch(int[] list,int len, int num){
|
||||
|
||||
int l=0,r=len-1,ans=len-1;
|
||||
while(l<=r){
|
||||
int mid=l+(r-l)/2;
|
||||
if(list[mid]<num){
|
||||
l=mid+1;
|
||||
}else{
|
||||
r=mid-1;
|
||||
ans=mid;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -227,7 +227,34 @@ class Solution:
|
||||
return dp[n]
|
||||
```
|
||||
Go:
|
||||
|
||||
```golang
|
||||
func integerBreak(n int) int {
|
||||
/**
|
||||
动态五部曲
|
||||
1.确定dp下标及其含义
|
||||
2.确定递推公式
|
||||
3.确定dp初始化
|
||||
4.确定遍历顺序
|
||||
5.打印dp
|
||||
**/
|
||||
dp:=make([]int,n+1)
|
||||
dp[1]=1
|
||||
dp[2]=1
|
||||
for i:=3;i<n+1;i++{
|
||||
for j:=1;j<i-1;j++{
|
||||
// i可以差分为i-j和j。由于需要最大值,故需要通过j遍历所有存在的值,取其中最大的值作为当前i的最大值,在求最大值的时候,一个是j与i-j相乘,一个是j与dp[i-j].
|
||||
dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]))
|
||||
}
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
func max(a,b int) int{
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
|
@ -162,21 +162,14 @@ class Solution:
|
||||
Do not return anything, modify s in-place instead.
|
||||
"""
|
||||
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]
|
||||
left += 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:
|
||||
@ -210,7 +203,20 @@ var reverseString = function(s) {
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func reverseString(_ s: inout [Character]) {
|
||||
var l = 0
|
||||
var r = s.count - 1
|
||||
while l < r {
|
||||
// 使用元祖
|
||||
(s[l], s[r]) = (s[r], s[l])
|
||||
l += 1
|
||||
r -= 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -121,13 +121,7 @@ Python:
|
||||
```python
|
||||
class Solution:
|
||||
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
|
||||
result_set = set()
|
||||
|
||||
set1 = set(nums1)
|
||||
for num in nums2:
|
||||
if num in set1:
|
||||
result_set.add(num) # set1里出现的nums2元素 存放到结果
|
||||
return list(result_set)
|
||||
return list(set(nums1) & set(nums2)) # 两个数组先变成集合,求交集后还原为数组
|
||||
```
|
||||
|
||||
|
||||
@ -149,6 +143,26 @@ func intersection(nums1 []int, nums2 []int) []int {
|
||||
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:
|
||||
|
||||
|
@ -186,27 +186,27 @@ Java:
|
||||
class Solution {
|
||||
public int eraseOverlapIntervals(int[][] intervals) {
|
||||
if (intervals.length < 2) return 0;
|
||||
|
||||
Arrays.sort(intervals, new Comparator<int[]>() {
|
||||
@Override
|
||||
public int compare(int[] o1, int[] o2) {
|
||||
if (o1[0] != o2[0]) {
|
||||
if (o1[1] != o2[1]) {
|
||||
return Integer.compare(o1[1],o2[1]);
|
||||
} else {
|
||||
return Integer.compare(o2[0],o1[0]);
|
||||
return Integer.compare(o1[0],o2[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
int count = 1;
|
||||
int edge = intervals[0][1];
|
||||
for (int i = 1; i < intervals.length; i++) {
|
||||
if (intervals[i][0] < edge) {
|
||||
count++;
|
||||
} else {
|
||||
if (edge <= intervals[i][0]){
|
||||
count ++; //non overlap + 1
|
||||
edge = intervals[i][1];
|
||||
}
|
||||
}
|
||||
return count;
|
||||
return intervals.length - count;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -19,15 +19,15 @@
|
||||
|
||||
示例:
|
||||
|
||||
输入:nums: [1, 1, 1, 1, 1], S: 3
|
||||
输出:5
|
||||
输入:nums: [1, 1, 1, 1, 1], S: 3
|
||||
输出: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。
|
||||
|
||||
@ -202,6 +202,7 @@ public:
|
||||
for (int i = 0; i < nums.size(); i++) sum += nums[i];
|
||||
if (S > sum) return 0; // 此时没有方案
|
||||
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
|
||||
if (S + sum < 0) return 0; // 以确保bagSize为正数
|
||||
int bagSize = (S + sum) / 2;
|
||||
vector<int> dp(bagSize + 1, 0);
|
||||
dp[0] = 1;
|
||||
@ -248,6 +249,7 @@ class Solution {
|
||||
for (int i = 0; i < nums.length; i++) sum += nums[i];
|
||||
if ((target + sum) % 2 != 0) return 0;
|
||||
int size = (target + sum) / 2;
|
||||
if(size < 0) size = -size;
|
||||
int[] dp = new int[size + 1];
|
||||
dp[0] = 1;
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
@ -311,7 +313,7 @@ Javascript:
|
||||
const findTargetSumWays = (nums, target) => {
|
||||
|
||||
const sum = nums.reduce((a, b) => a+b);
|
||||
|
||||
|
||||
if(target > sum) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -155,34 +155,27 @@ class Solution {
|
||||
|
||||
Python:
|
||||
```python
|
||||
|
||||
class Solution(object):
|
||||
def reverseStr(self, s, k):
|
||||
class Solution:
|
||||
def reverseStr(self, s: str, k: int) -> str:
|
||||
"""
|
||||
:type s: str
|
||||
:type k: int
|
||||
:rtype: str
|
||||
1. 使用range(start, end, step)来确定需要调换的初始位置
|
||||
2. 对于字符串s = 'abc',如果使用s[0:999] ===> 'abc'。字符串末尾如果超过最大长度,则会返回至字符串最后一个值,这个特性可以避免一些边界条件的处理。
|
||||
3. 用切片整体替换,而不是一个个替换.
|
||||
"""
|
||||
from functools import reduce
|
||||
# turn s into a list
|
||||
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
|
||||
def reverse_substring(text):
|
||||
left, right = 0, len(text) - 1
|
||||
while left < right:
|
||||
s[left], s[right] = s[right], s[left]
|
||||
text[left], text[right] = text[right], text[left]
|
||||
left += 1
|
||||
right -= 1
|
||||
return s
|
||||
return text
|
||||
|
||||
# make sure we reverse each 2k elements
|
||||
for i in range(0, len(s), 2*k):
|
||||
s[i:(i+k)] = reverse(s[i:(i+k)])
|
||||
|
||||
# combine list into str.
|
||||
return reduce(lambda a, b: a+b, s)
|
||||
res = list(s)
|
||||
|
||||
for cur in range(0, len(s), 2 * k):
|
||||
res[cur: cur + k] = reverse_substring(res[cur: cur + k])
|
||||
|
||||
return ''.join(res)
|
||||
```
|
||||
|
||||
|
||||
|
@ -949,6 +949,89 @@ class MyLinkedList {
|
||||
```
|
||||
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
class MyLinkedList {
|
||||
var dummyHead: ListNode<Int>?
|
||||
var size: Int
|
||||
|
||||
init() {
|
||||
dummyHead = ListNode(0)
|
||||
size = 0
|
||||
}
|
||||
|
||||
func get(_ index: Int) -> Int {
|
||||
if index >= size || index < 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
var curNode = dummyHead?.next
|
||||
var curIndex = index
|
||||
|
||||
while curIndex > 0 {
|
||||
curNode = curNode?.next
|
||||
curIndex -= 1
|
||||
}
|
||||
|
||||
return curNode?.value ?? -1
|
||||
}
|
||||
|
||||
func addAtHead(_ val: Int) {
|
||||
let newHead = ListNode(val)
|
||||
newHead.next = dummyHead?.next
|
||||
dummyHead?.next = newHead
|
||||
size += 1
|
||||
}
|
||||
|
||||
func addAtTail(_ val: Int) {
|
||||
let newNode = ListNode(val)
|
||||
var curNode = dummyHead
|
||||
while curNode?.next != nil {
|
||||
curNode = curNode?.next
|
||||
}
|
||||
|
||||
curNode?.next = newNode
|
||||
size += 1
|
||||
}
|
||||
|
||||
func addAtIndex(_ index: Int, _ val: Int) {
|
||||
if index > size {
|
||||
return
|
||||
}
|
||||
|
||||
let newNode = ListNode(val)
|
||||
var curNode = dummyHead
|
||||
var curIndex = index
|
||||
|
||||
while curIndex > 0 {
|
||||
curNode = curNode?.next
|
||||
curIndex -= 1
|
||||
}
|
||||
|
||||
newNode.next = curNode?.next
|
||||
curNode?.next = newNode
|
||||
size += 1
|
||||
}
|
||||
|
||||
func deleteAtIndex(_ index: Int) {
|
||||
if index >= size || index < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -89,7 +89,16 @@ class Solution {
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
```python3
|
||||
class Solution:
|
||||
def pivotIndex(self, nums: List[int]) -> int:
|
||||
numSum = sum(nums) #数组总和
|
||||
leftSum = 0
|
||||
for i in range(len(nums)):
|
||||
if numSum - leftSum -nums[i] == leftSum: #左右和相等
|
||||
return i
|
||||
leftSum += nums[i]
|
||||
return -1
|
||||
```
|
||||
|
||||
## Go
|
||||
|
@ -149,7 +149,32 @@ class Solution {
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
```python3
|
||||
#方法2
|
||||
class Solution:
|
||||
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
|
||||
result = [0]*len(nums)
|
||||
evenIndex = 0
|
||||
oddIndex = 1
|
||||
for i in range(len(nums)):
|
||||
if nums[i] % 2: #奇数
|
||||
result[oddIndex] = nums[i]
|
||||
oddIndex += 2
|
||||
else: #偶数
|
||||
result[evenIndex] = nums[i]
|
||||
evenIndex += 2
|
||||
return result
|
||||
|
||||
#方法3
|
||||
class Solution:
|
||||
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
|
||||
oddIndex = 1
|
||||
for i in range(0,len(nums),2): #步长为2
|
||||
if nums[i] % 2: #偶数位遇到奇数
|
||||
while nums[oddIndex] % 2: #奇数位找偶数
|
||||
oddIndex += 2
|
||||
nums[i], nums[oddIndex] = nums[oddIndex], nums[i]
|
||||
return nums
|
||||
```
|
||||
|
||||
## Go
|
||||
|
@ -252,6 +252,24 @@ func sortedSquares(_ nums: [Int]) -> [Int] {
|
||||
}
|
||||
```
|
||||
|
||||
Ruby:
|
||||
|
||||
```ruby
|
||||
def sorted_squares(nums)
|
||||
left, right, result = 0, nums.size - 1, []
|
||||
while left <= right
|
||||
if nums[left]**2 > nums[right]**2
|
||||
result << nums[left]**2
|
||||
left += 1
|
||||
else
|
||||
result << nums[right]**2
|
||||
right -= 1
|
||||
end
|
||||
end
|
||||
result.reverse
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -233,7 +233,41 @@ var commonChars = function (words) {
|
||||
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)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -197,15 +197,38 @@ class Solution {
|
||||
|
||||
Python:
|
||||
```python3
|
||||
# 方法一,使用栈,推荐!
|
||||
class Solution:
|
||||
def removeDuplicates(self, s: str) -> str:
|
||||
t = list()
|
||||
for i in s:
|
||||
if t and t[-1] == i:
|
||||
t.pop(-1)
|
||||
res = list()
|
||||
for item in s:
|
||||
if res and res[-1] == item:
|
||||
res.pop()
|
||||
else:
|
||||
t.append(i)
|
||||
return "".join(t) # 字符串拼接
|
||||
res.append(item)
|
||||
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:
|
||||
|
@ -202,45 +202,27 @@ func replaceSpace(s string) string {
|
||||
|
||||
python:
|
||||
```python
|
||||
class Solution(object):
|
||||
def replaceSpace(self, s):
|
||||
"""
|
||||
: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
|
||||
class Solution:
|
||||
def replaceSpace(self, s: str) -> str:
|
||||
counter = s.count(' ')
|
||||
|
||||
# 将list变回str,输出
|
||||
s = ''.join(list_s)
|
||||
return s
|
||||
res = list(s)
|
||||
# 每碰到一个空格就多拓展两个格子,1 + 2 = 3个位置存’%20‘
|
||||
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:
|
||||
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||
return s[n:] + s[0:n]
|
||||
|
||||
```
|
||||
```python
|
||||
# 方法二:也可以使用上文描述的方法,有些面试中不允许使用切片,那就使用上文作者提到的方法
|
||||
# class Solution:
|
||||
# def reverseLeftWords(self, s: str, n: int) -> str:
|
||||
# s = list(s)
|
||||
# s[0:n] = list(reversed(s[0:n]))
|
||||
# s[n:] = list(reversed(s[n:]))
|
||||
# s.reverse()
|
||||
class Solution:
|
||||
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||
s = list(s)
|
||||
s[0:n] = list(reversed(s[0:n]))
|
||||
s[n:] = list(reversed(s[n:]))
|
||||
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),python的string为不可变,需要开辟同样大小的list空间来修改
|
||||
|
||||
```
|
||||
|
||||
```python 3
|
||||
#方法三:考虑不能用切片的情况下,利用模+下标实现
|
||||
#方法四:考虑不能用切片的情况下,利用模+下标实现
|
||||
class Solution:
|
||||
def reverseLeftWords(self, s: str, n: int) -> str:
|
||||
new_s = ''
|
||||
|
@ -103,7 +103,7 @@ dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包
|
||||
|
||||
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
|
||||
|
||||
当j >= weight[0]是,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||
|
||||
代码初始化如下:
|
||||
```
|
||||
|
@ -160,34 +160,21 @@ Python:
|
||||
|
||||
class Solution:
|
||||
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
|
||||
lengthA,lengthB = 0,0
|
||||
curA,curB = headA,headB
|
||||
while(curA!=None): #求链表A的长度
|
||||
curA = curA.next
|
||||
lengthA +=1
|
||||
|
||||
while(curB!=None): #求链表B的长度
|
||||
curB = curB.next
|
||||
lengthB +=1
|
||||
|
||||
curA, curB = headA, headB
|
||||
"""
|
||||
根据快慢法则,走的快的一定会追上走得慢的。
|
||||
在这道题里,有的链表短,他走完了就去走另一条链表,我们可以理解为走的快的指针。
|
||||
|
||||
if lengthB>lengthA: #让curA为最长链表的头,lenA为其长度
|
||||
lengthA, lengthB = lengthB, lengthA
|
||||
curA, curB = curB, curA
|
||||
那么,只要其中一个链表走完了,就去走另一条链表的路。如果有交点,他们最终一定会在同一个
|
||||
位置相遇
|
||||
"""
|
||||
cur_a, cur_b = headA, headB # 用两个指针代替a和b
|
||||
|
||||
gap = lengthA - lengthB #求长度差
|
||||
while(gap!=0):
|
||||
curA = curA.next #让curA和curB在同一起点上
|
||||
gap -= 1
|
||||
|
||||
while(curA!=None):
|
||||
if curA == curB:
|
||||
return curA
|
||||
else:
|
||||
curA = curA.next
|
||||
curB = curB.next
|
||||
return None
|
||||
while cur_a != cur_b:
|
||||
cur_a = cur_a.next if cur_a else headB # 如果a走完了,那么就切换到b走
|
||||
cur_b = cur_b.next if cur_b else headA # 同理,b走完了就切换到a
|
||||
|
||||
return cur_a
|
||||
```
|
||||
|
||||
Go:
|
||||
|
Reference in New Issue
Block a user