diff --git a/README.md b/README.md
index c006fd6b..96f7bb9c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-## 一些闲话:
+👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定)
+👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master)
> 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者)
> 2. **PDF版本** : [「代码随想录」算法精讲 PDF 版本](https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ) 。
@@ -87,6 +88,7 @@
* 编程语言
* [C++面试&C++学习指南知识点整理](https://github.com/youngyangyang04/TechCPP)
+
* 项目
* [基于跳表的轻量级KV存储引擎](https://github.com/youngyangyang04/Skiplist-CPP)
* [Nosql数据库注入攻击系统](https://github.com/youngyangyang04/NoSQLAttack)
@@ -95,6 +97,7 @@
* [看了这么多代码,谈一谈代码风格!](./problems/前序/代码风格.md)
* [力扣上的代码想在本地编译运行?](./problems/前序/力扣上的代码想在本地编译运行?.md)
* [什么是核心代码模式,什么又是ACM模式?](./problems/前序/什么是核心代码模式,什么又是ACM模式?.md)
+
* 工具
* [一站式vim配置](https://github.com/youngyangyang04/PowerVim)
* [保姆级Git入门教程,万字详解](https://mp.weixin.qq.com/s/Q_O0ey4C9tryPZaZeJocbA)
@@ -119,27 +122,33 @@
* [递归算法的时间与空间复杂度分析!](./problems/前序/递归算法的时间与空间复杂度分析.md)
* [刷了这么多题,你了解自己代码的内存消耗么?](./problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md)
-(持续更新中.....)
-
## 知识星球精选
-1. [选择方向的时候,我也迷茫了](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g)
-2. [刷题就用库函数了,怎么了?](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg)
-3. [关于实习,大家可能有点迷茫!](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA)
-4. [马上秋招了,慌得很!](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA)
-5. [Carl看了上百份简历,总结了这些!](https://mp.weixin.qq.com/s/sJa87MZD28piCOVMFkIbwQ)
-6. [面试中遇到了发散性问题.....](https://mp.weixin.qq.com/s/SSonDxi2pjkSVwHNzZswng)
-7. [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw)
-8. [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg)
-9. [秋招和提前批都越来越提前了....](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ)
-10. [你的简历里「专业技能」写的够专业么?](https://mp.weixin.qq.com/s/bp6y-e5FVN28H9qc8J9zrg)
-11. [对于秋招,实习生也有烦恼....](https://mp.weixin.qq.com/s/ka07IPryFnfmIjByFFcXDg)
-12. [华为提前批已经开始了.....](https://mp.weixin.qq.com/s/OC35QDG8pn5OwLpCxieStw)
-13. [大厂新人培养体系应该是什么样的?](https://mp.weixin.qq.com/s/WBaPCosOljB5NEkFL2GhOQ)
+* [为什么都说客户端会消失](./problems/知识星球精选/客三消.md)
+* [博士转计算机如何找工作](./problems/知识星球精选/博士转行计算机.md)
+* [不一样的七夕](./problems/知识星球精选/不一样的七夕.md)
+* [HR面注意事项](./problems/知识星球精选/HR面注意事项.md)
+* [刷题攻略要刷两遍!](./problems/知识星球精选/刷题攻略要刷两遍.md)
+* [秋招进行中的迷茫与焦虑......](./problems/知识星球精选/秋招进行中的迷茫与焦虑.md)
+* [大厂新人培养体系应该是什么样的?](./problems/知识星球精选/大厂新人培养体系.md)
+* [你的简历里「专业技能」写的够专业么?](./problems/知识星球精选/专业技能可以这么写.md)
+* [Carl看了上百份简历,总结了这些!](./problems/知识星球精选/写简历的一些问题.md)
+* [备战2022届秋招](./problems/知识星球精选/备战2022届秋招.md)
+* [技术不太好,如果选择方向](./problems/知识星球精选/技术不好如何选择技术方向.md)
+* [刷题要不要使用库函数](./problems/知识星球精选/刷力扣用不用库函数.md)
+* [关于实习的几点问题](./problems/知识星球精选/关于实习大家的疑问.md)
+* [面试中遇到了发散性问题,怎么办?](./problems/知识星球精选/面试中发散性问题.md)
+* [英语到底重不重要!](./problems/知识星球精选/英语到底重不重要.md)
+* [计算机专业要不要读研!](./problems/知识星球精选/要不要考研.md)
+* [关于提前批的一些建议](./problems/知识星球精选/关于提前批的一些建议.md)
+* [已经在实习的录友要如何准备秋招](./problems/知识星球精选/如何权衡实习与秋招复习.md)
+* [华为提前批已经开始了](./problems/知识星球精选/提前批已经开始了.md)
## 杂谈
+* [「代码随想录」刷题网站上线](https://mp.weixin.qq.com/s/-6rd_g7LrVD1fuKBYk2tXQ)。
* [LeetCode-Master上榜了](https://mp.weixin.qq.com/s/wZRTrA9Rbvgq1yEkSw4vfQ)
+* [上榜之后,都有哪些变化?](https://mp.weixin.qq.com/s/VJBV0qSBthjnbbmW-lctLA)
* [大半年过去了......](https://mp.weixin.qq.com/s/lubfeistPxBLSQIe5XYg5g)
* [一万录友在B站学算法!](https://mp.weixin.qq.com/s/Vzq4zkMZY7erKeu0fqGLgw)
@@ -241,7 +250,7 @@
15. [二叉树:以为使用了递归,其实还隐藏着回溯](./problems/二叉树中递归带着回溯.md)
16. [二叉树:做了这么多题目了,我的左叶子之和是多少?](./problems/0404.左叶子之和.md)
17. [二叉树:我的左下角的值是多少?](./problems/0513.找树左下角的值.md)
-18. [二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](./problems/0112.路径总和.md)
+18. [二叉树:路径总和](./problems/0112.路径总和.md)
19. [二叉树:构造二叉树登场!](./problems/0106.从中序与后序遍历序列构造二叉树.md)
20. [二叉树:构造一棵最大的二叉树](./problems/0654.最大二叉树.md)
21. [本周小结!(二叉树系列三)](./problems/周总结/20201010二叉树周末总结.md)
@@ -409,6 +418,7 @@
2. [单调栈:下一个更大元素I](./problems/0496.下一个更大元素I.md)
3. [单调栈:下一个更大元素II](./problems/0503.下一个更大元素II.md)
4. [单调栈:接雨水](./problems/0042.接雨水.md)
+5. [单调栈:柱状图中最大的矩形](./problems/0084.柱状图中最大的矩形.md)
(持续更新中....)
@@ -451,7 +461,6 @@
* [24.两两交换链表中的节点](./problems/0024.两两交换链表中的节点.md)
* [234.回文链表](./problems/0234.回文链表.md)
* [143.重排链表](./problems/0143.重排链表.md)【数组】【双向队列】【直接操作链表】
-* [234.回文链表](./problems/0234.回文链表.md)
* [141.环形链表](./problems/0141.环形链表.md)
## 哈希表
@@ -467,8 +476,14 @@
* [100.相同的树](./problems/0100.相同的树.md) 同101.对称二叉树 一个思路
* [116.填充每个节点的下一个右侧节点指针](./problems/0116.填充每个节点的下一个右侧节点指针.md)
+## 回溯算法
+
+* [52.N皇后II](./problems/0052.N皇后II.md)
+
+
## 贪心
* [649.Dota2参议院](./problems/0649.Dota2参议院.md) 有难度
+* [1221.分割平衡字符](./problems/1221.分割平衡字符串.md) 简单贪心
## 动态规划
* [5.最长回文子串](./problems/0005.最长回文子串.md) 和[647.回文子串](https://mp.weixin.qq.com/s/2WetyP6IYQ6VotegepVpEw) 差不多是一样的
@@ -478,6 +493,7 @@
## 图论
* [463.岛屿的周长](./problems/0463.岛屿的周长.md) (模拟)
* [841.钥匙和房间](./problems/0841.钥匙和房间.md) 【有向图】dfs,bfs都可以
+* [127.单词接龙](./problems/0127.单词接龙.md) 广搜
## 并查集
* [684.冗余连接](./problems/0684.冗余连接.md) 【并查集基础题目】
diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md
index 5be94996..a6381eff 100644
--- a/problems/0001.两数之和.md
+++ b/problems/0001.两数之和.md
@@ -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 效率更高!**
@@ -62,7 +62,7 @@ std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底
C++代码:
-```C++
+```CPP
class Solution {
public:
vector twoSum(vector& nums, int target) {
@@ -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] # 如果存在就返回字典记录索引和当前索引
```
@@ -205,9 +206,51 @@ function twoSum(array $nums, int $target): array
}
```
+Swift:
+```swift
+func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
+ var res = [Int]()
+ var dict = [Int : Int]()
+ for i in 0 ..< nums.count {
+ let other = target - nums[i]
+ if dict.keys.contains(other) {
+ res.append(i)
+ res.append(dict[other]!)
+ return res
+ }
+ dict[nums[i]] = i
+ }
+ return res
+}
+```
+
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer[] $nums
+ * @param Integer $target
+ * @return Integer[]
+ */
+ function twoSum($nums, $target) {
+ if (count($nums) == 0) {
+ return [];
+ }
+ $table = [];
+ for ($i = 0; $i < count($nums); $i++) {
+ $temp = $target - $nums[$i];
+ if (isset($table[$temp])) {
+ return [$table[$temp], $i];
+ }
+ $table[$nums[$i]] = $i;
+ }
+ return [];
+ }
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0005.最长回文子串.md b/problems/0005.最长回文子串.md
index 0063b358..c78b827c 100644
--- a/problems/0005.最长回文子串.md
+++ b/problems/0005.最长回文子串.md
@@ -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.回文子串
## 暴力解法
@@ -67,7 +67,7 @@
以上三种情况分析完了,那么递归公式如下:
-```C++
+```CPP
if (s[i] == s[j]) {
if (j - i <= 1) { // 情况一 和 情况二
dp[i][j] = true;
@@ -81,7 +81,7 @@ if (s[i] == s[j]) {
在得到[i,j]区间是否是回文子串的时候,直接保存最长回文子串的左边界和右边界,代码如下:
-```C++
+```CPP
if (s[i] == s[j]) {
if (j - i <= 1) { // 情况一 和 情况二
dp[i][j] = true;
@@ -120,7 +120,7 @@ dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图:
代码如下:
-```C++
+```CPP
for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序
for (int j = i; j < s.size(); j++) {
if (s[i] == s[j]) {
@@ -150,7 +150,7 @@ for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
string longestPalindrome(string s) {
@@ -181,7 +181,7 @@ public:
```
以上代码是为了凸显情况一二三,当然是可以简洁一下的,如下:
-```C++
+```CPP
class Solution {
public:
string longestPalindrome(string s) {
@@ -226,7 +226,7 @@ public:
**这两种情况可以放在一起计算,但分别计算思路更清晰,我倾向于分别计算**,代码如下:
-```C++
+```CPP
class Solution {
public:
int left = 0;
@@ -270,6 +270,23 @@ public:
## Python
```python
+class Solution:
+ def longestPalindrome(self, s: str) -> str:
+ dp = [[False] * len(s) for _ in range(len(s))]
+ maxlenth = 0
+ left = 0
+ right = 0
+ for i in range(len(s) - 1, -1, -1):
+ for j in range(i, len(s)):
+ if s[j] == s[i]:
+ if j - i <= 1 or dp[i + 1][j - 1]:
+ dp[i][j] = True
+ if dp[i][j] and j - i + 1 > maxlenth:
+ maxlenth = j - i + 1
+ left = i
+ right = j
+ return s[left:right + 1]
+
```
## Go
@@ -286,6 +303,6 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0015.三数之和.md b/problems/0015.三数之和.md
index 36abb58c..6b5311ae 100644
--- a/problems/0015.三数之和.md
+++ b/problems/0015.三数之和.md
@@ -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中,然后再去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
@@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/3sum/
大家可以尝试使用哈希法写一写,就知道其困难的程度了。
哈希法C++代码:
-```C++
+```CPP
class Solution {
public:
vector> threeSum(vector& nums) {
@@ -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相遇为止。
@@ -107,7 +107,7 @@ public:
C++代码代码如下:
-```C++
+```CPP
class Solution {
public:
vector> threeSum(vector& nums) {
@@ -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)要求返回的是数值的话,就可以使用双指针法了。
@@ -393,10 +393,90 @@ function threeSum(array $nums): array
}
```
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer[] $nums
+ * @return Integer[][]
+ */
+ function threeSum($nums) {
+ $res = [];
+ sort($nums);
+ for ($i = 0; $i < count($nums); $i++) {
+ if ($nums[$i] > 0) {
+ return $res;
+ }
+ if ($i > 0 && $nums[$i] == $nums[$i - 1]) {
+ continue;
+ }
+ $left = $i + 1;
+ $right = count($nums) - 1;
+ while ($left < $right) {
+ $sum = $nums[$i] + $nums[$left] + $nums[$right];
+ if ($sum < 0) {
+ $left++;
+ }
+ else if ($sum > 0) {
+ $right--;
+ }
+ else {
+ $res[] = [$nums[$i], $nums[$left], $nums[$right]];
+ while ($left < $right && $nums[$left] == $nums[$left + 1]) $left++;
+ while ($left < $right && $nums[$right] == $nums[$right - 1]) $right--;
+ $left++;
+ $right--;
+ }
+ }
+ }
+ return $res;
+ }
+}
+```
+Swift:
+```swift
+// 双指针法
+func threeSum(_ nums: [Int]) -> [[Int]] {
+ var res = [[Int]]()
+ var sorted = nums
+ sorted.sort()
+ for i in 0 ..< sorted.count {
+ if sorted[i] > 0 {
+ return res
+ }
+ if i > 0 && sorted[i] == sorted[i - 1] {
+ continue
+ }
+ var left = i + 1
+ var right = sorted.count - 1
+ while left < right {
+ let sum = sorted[i] + sorted[left] + sorted[right]
+ if sum < 0 {
+ left += 1
+ } else if sum > 0 {
+ right -= 1
+ } else {
+ res.append([sorted[i], sorted[left], sorted[right]])
+
+ while left < right && sorted[left] == sorted[left + 1] {
+ left += 1
+ }
+ while left < right && sorted[right] == sorted[right - 1] {
+ right -= 1
+ }
+
+ left += 1
+ right -= 1
+ }
+ }
+ }
+ return res
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md
index 1562052c..1221115c 100644
--- a/problems/0017.电话号码的字母组合.md
+++ b/problems/0017.电话号码的字母组合.md
@@ -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)的区别,本题是多个集合求组合,所以在回溯的搜索过程中,都有一些细节需要注意的。
其实本题不算难,但也处处是细节,大家还要自己亲自动手写一写。
@@ -322,20 +322,20 @@ python3:
```py
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
- self.s = ""
res = []
+ s = ""
letterMap = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
- if len(digits) == 0: return res
- def backtrack(digits,index):
+ if not len(digits): return res
+ def backtrack(digits,index, s):
if index == len(digits):
- return res.append(self.s)
+ return res.append(s)
digit = int(digits[index]) #将index指向的数字转为int
letters = letterMap[digit] #取数字对应的字符集
for i in range(len(letters)):
- self.s += letters[i]
- backtrack(digits,index + 1) #递归,注意index+1,一下层要处理下一个数字
- self.s = self.s[:-1] #回溯
- backtrack(digits,0)
+ s += letters[i]
+ backtrack(digits, index+1, s) #递归,注意index+1,一下层要处理下一个数字
+ s = s[:-1] #回溯
+ backtrack(digits, 0, s)
return res
```
@@ -410,10 +410,70 @@ var letterCombinations = function(digits) {
};
```
+C:
+```c
+char* path;
+int pathTop;
+char** result;
+int resultTop;
+char* letterMap[10] = {"", //0
+ "", //1
+ "abc", //2
+ "def", //3
+ "ghi", //4
+ "jkl", //5
+ "mno", //6
+ "pqrs", //7
+ "tuv", //8
+ "wxyz", //9
+};
+void backTracking(char* digits, int index) {
+ //若当前下标等于digits数组长度
+ if(index == strlen(digits)) {
+ //复制digits数组,因为最后要多存储一个0,所以数组长度要+1
+ char* tempString = (char*)malloc(sizeof(char) * strlen(digits) + 1);
+ int j;
+ for(j = 0; j < strlen(digits); j++) {
+ tempString[j] = path[j];
+ }
+ //char数组最后要以0结尾
+ tempString[strlen(digits)] = 0;
+ result[resultTop++] = tempString;
+ return ;
+ }
+ //将字符数字转换为真的数字
+ int digit = digits[index] - '0';
+ //找到letterMap中对应的字符串
+ char* letters = letterMap[digit];
+ int i;
+ for(i = 0; i < strlen(letters); i++) {
+ path[pathTop++] = letters[i];
+ //递归,处理下一层数字
+ backTracking(digits, index+1);
+ pathTop--;
+ }
+}
+
+char ** letterCombinations(char * digits, int* returnSize){
+ //初始化path和result
+ path = (char*)malloc(sizeof(char) * strlen(digits));
+ result = (char**)malloc(sizeof(char*) * 300);
+
+ *returnSize = 0;
+ //若digits数组中元素个数为0,返回空集
+ if(strlen(digits) == 0)
+ return result;
+ pathTop = resultTop = 0;
+ backTracking(digits, 0);
+ *returnSize = resultTop;
+
+ return result;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0018.四数之和.md b/problems/0018.四数之和.md
index 0caf12be..9891f0fe 100644
--- a/problems/0018.四数之和.md
+++ b/problems/0018.四数之和.md
@@ -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,43 +31,43 @@ 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)
双指针法在字符串题目中还有很多应用,后面还会介绍到。
C++代码
-```C++
+```CPP
class Solution {
public:
vector> fourSum(vector& nums, int target) {
@@ -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):
"""
@@ -201,6 +227,54 @@ class Solution(object):
```
Go:
+```go
+func fourSum(nums []int, target int) [][]int {
+ if len(nums) < 4 {
+ return nil
+ }
+ sort.Ints(nums)
+ var res [][]int
+ for i := 0; i < len(nums)-3; i++ {
+ n1 := nums[i]
+ // if n1 > target { // 不能这样写,因为可能是负数
+ // break
+ // }
+ if i > 0 && n1 == nums[i-1] {
+ continue
+ }
+ for j := i + 1; j < len(nums)-2; j++ {
+ n2 := nums[j]
+ if j > i+1 && n2 == nums[j-1] {
+ continue
+ }
+ l := j + 1
+ r := len(nums) - 1
+ for l < r {
+ n3 := nums[l]
+ n4 := nums[r]
+ sum := n1 + n2 + n3 + n4
+ if sum < target {
+ l++
+ } else if sum > target {
+ r--
+ } else {
+ res = append(res, []int{n1, n2, n3, n4})
+ for l < r && n3 == nums[l+1] { // 去重
+ l++
+ }
+ for l < r && n4 == nums[r-1] { // 去重
+ r--
+ }
+ // 找到答案时,双指针同时靠近
+ r--
+ l++
+ }
+ }
+ }
+ }
+ return res
+}
+```
javaScript:
@@ -236,9 +310,100 @@ var fourSum = function(nums, target) {
};
```
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer[] $nums
+ * @param Integer $target
+ * @return Integer[][]
+ */
+ function fourSum($nums, $target) {
+ $res = [];
+ sort($nums);
+ for ($i = 0; $i < count($nums); $i++) {
+ if ($i > 0 && $nums[$i] == $nums[$i - 1]) {
+ continue;
+ }
+ for ($j = $i + 1; $j < count($nums); $j++) {
+ if ($j > $i + 1 && $nums[$j] == $nums[$j - 1]) {
+ continue;
+ }
+ $left = $j + 1;
+ $right = count($nums) - 1;
+ while ($left < $right) {
+ $sum = $nums[$i] + $nums[$j] + $nums[$left] + $nums[$right];
+ if ($sum < $target) {
+ $left++;
+ }
+ else if ($sum > $target) {
+ $right--;
+ }
+ else {
+ $res[] = [$nums[$i], $nums[$j], $nums[$left], $nums[$right]];
+ while ($left < $right && $nums[$left] == $nums[$left+1]) $left++;
+ while ($left < $right && $nums[$right] == $nums[$right-1]) $right--;
+ $left++;
+ $right--;
+ }
+ }
+ }
+ }
+ return $res;
+ }
+}
+```
+
+Swift:
+```swift
+func fourSum(_ nums: [Int], _ target: Int) -> [[Int]] {
+ var res = [[Int]]()
+ var sorted = nums
+ sorted.sort()
+ for k in 0 ..< sorted.count {
+ // 这种剪枝不行,target可能是负数
+// if sorted[k] > target {
+// return res
+// }
+ // 去重
+ if k > 0 && sorted[k] == sorted[k - 1] {
+ continue
+ }
+
+ let target2 = target - sorted[k]
+ for i in (k + 1) ..< sorted.count {
+ if i > (k + 1) && sorted[i] == sorted[i - 1] {
+ continue
+ }
+ var left = i + 1
+ var right = sorted.count - 1
+ while left < right {
+ let sum = sorted[i] + sorted[left] + sorted[right]
+ if sum < target2 {
+ left += 1
+ } else if sum > target2 {
+ right -= 1
+ } else {
+ res.append([sorted[k], sorted[i], sorted[left], sorted[right]])
+ while left < right && sorted[left] == sorted[left + 1] {
+ left += 1
+ }
+ while left < right && sorted[right] == sorted[right - 1] {
+ right -= 1
+ }
+ // 找到答案 双指针同时收缩
+ left += 1
+ right -= 1
+ }
+ }
+ }
+ }
+ return res
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0019.删除链表的倒数第N个节点.md b/problems/0019.删除链表的倒数第N个节点.md
index 52735794..85e3eb48 100644
--- a/problems/0019.删除链表的倒数第N个节点.md
+++ b/problems/0019.删除链表的倒数第N个节点.md
@@ -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指针,初始值为虚拟头结点,如图:
@@ -58,7 +58,7 @@
此时不难写出如下C++代码:
-```C++
+```CPP
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
@@ -184,9 +184,53 @@ var removeNthFromEnd = function(head, n) {
return ret.next;
};
```
+Kotlin:
+```Kotlin
+fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? {
+ val pre = ListNode(0).apply {
+ this.next = head
+ }
+ var fastNode: ListNode? = pre
+ var slowNode: ListNode? = pre
+ for (i in 0..n) {
+ fastNode = fastNode?.next
+ }
+ while (fastNode != null) {
+ slowNode = slowNode?.next
+ fastNode = fastNode.next
+ }
+ slowNode?.next = slowNode?.next?.next
+ return pre.next
+}
+```
+
+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)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0020.有效的括号.md b/problems/0020.有效的括号.md
index 178bc6f8..5b02f9ee 100644
--- a/problems/0020.有效的括号.md
+++ b/problems/0020.有效的括号.md
@@ -12,7 +12,7 @@
# 20. 有效的括号
-https://leetcode-cn.com/problems/valid-parentheses/
+[力扣题目链接](https://leetcode-cn.com/problems/valid-parentheses/)
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
@@ -108,7 +108,7 @@ cd a/b/c/../../
实现C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool isValid(string s) {
@@ -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:
@@ -264,4 +290,4 @@ var isValid = function(s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0024.两两交换链表中的节点.md b/problems/0024.两两交换链表中的节点.md
index 66e149e6..75a5f739 100644
--- a/problems/0024.两两交换链表中的节点.md
+++ b/problems/0024.两两交换链表中的节点.md
@@ -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)。
接下来就是交换相邻两个元素了,**此时一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序**
@@ -43,7 +43,7 @@ https://leetcode-cn.com/problems/swap-nodes-in-pairs/
对应的C++代码实现如下: (注释中详细和如上图中的三步做对应)
-```C++
+```CPP
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
@@ -86,6 +86,34 @@ public:
## 其他语言版本
+C:
+```
+/**
+ * Definition for singly-linked list.
+ * struct ListNode {
+ * int val;
+ * struct ListNode *next;
+ * };
+ */
+
+
+struct ListNode* swapPairs(struct ListNode* head){
+ //使用双指针避免使用中间变量
+ typedef struct ListNode ListNode;
+ ListNode *fakehead = (ListNode *)malloc(sizeof(ListNode));
+ fakehead->next = head;
+ ListNode* right = fakehead->next;
+ ListNode* left = fakehead;
+ while(left && right && right->next ){
+ left->next = right->next;
+ right->next = left->next->next;
+ left->next->next = right;
+ left = right;
+ right = left->next;
+ }
+ return fakehead->next;
+}
+```
Java:
@@ -132,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:
@@ -200,9 +236,51 @@ var swapPairs = function (head) {
};
```
+Kotlin:
+
+```kotlin
+fun swapPairs(head: ListNode?): ListNode? {
+ val dummyNode = ListNode(0).apply {
+ this.next = head
+ }
+ var cur: ListNode? = dummyNode
+ while (cur?.next != null && cur.next?.next != null) {
+ val temp = cur.next
+ val temp2 = cur.next?.next?.next
+ cur.next = cur.next?.next
+ cur.next?.next = temp
+ cur.next?.next?.next = temp2
+ cur = cur.next?.next
+ }
+ return dummyNode.next
+}
+```
+
+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
+}
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md
index f1187db7..78f9afb7 100644
--- a/problems/0027.移除元素.md
+++ b/problems/0027.移除元素.md
@@ -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)。
### 暴力解法
@@ -48,7 +48,7 @@
代码如下:
-```C++
+```CPP
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
@@ -85,7 +85,7 @@ public:
后序都会一一介绍到,本题代码如下:
-```C++
+```CPP
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
@@ -106,7 +106,7 @@ public:
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$
-旧文链接:[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
+旧文链接:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)
## 相关题目推荐
@@ -144,15 +144,28 @@ class Solution {
Python:
-```python
+```python3
class Solution:
- def removeElement(self, nums: List[int], val: int) -> int:
- i,n = 0,len(nums)
- for j in range(n):
- if nums[j] != val:
- nums[i] = nums[j]
- i += 1
- return i
+ """双指针法
+ 时间复杂度:O(n)
+ 空间复杂度:O(1)
+ """
+
+ @classmethod
+ def removeElement(cls, nums: List[int], val: int) -> int:
+ fast = slow = 0
+
+ while fast < len(nums):
+
+ if nums[fast] != val:
+ nums[slow] = nums[fast]
+ slow += 1
+
+ # 当 fast 指针遇到要删除的元素时停止赋值
+ # slow 指针停止移动, fast 指针继续前进
+ fast += 1
+
+ return slow
```
@@ -201,23 +214,80 @@ end
```
Rust:
```rust
-pub fn remove_element(nums: &mut Vec, val: i32) -> &mut Vec {
- let mut start: usize = 0;
- while start < nums.len() {
- if nums[start] == val {
- nums.remove(start);
+impl Solution {
+ pub fn remove_element(nums: &mut Vec, val: i32) -> i32 {
+ let mut slowIdx = 0;
+ for pos in (0..nums.len()) {
+ if nums[pos]!=val {
+ nums[slowIdx] = nums[pos];
+ slowIdx += 1;
+ }
}
- start += 1;
+ return (slowIdx) as i32;
}
- nums
-}
-fn main() {
- let mut nums = vec![5,1,3,5,2,3,4,1];
- println!("{:?}",remove_element(&mut nums, 5));
}
```
+
+Swift:
+
+```swift
+func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
+ var slowIndex = 0
+
+ for fastIndex in 0..
+
diff --git a/problems/0028.实现strStr.md b/problems/0028.实现strStr.md
index fc14a34a..481be2ed 100644
--- a/problems/0028.实现strStr.md
+++ b/problems/0028.实现strStr.md
@@ -11,7 +11,7 @@
# 28. 实现 strStr()
-https://leetcode-cn.com/problems/implement-strstr/
+[力扣题目链接](https://leetcode-cn.com/problems/implement-strstr/)
实现 strStr() 函数。
@@ -315,7 +315,7 @@ next[i] = j;
最后整体构建next数组的函数代码如下:
-```C++
+```CPP
void getNext(int* next, const string& s){
int j = -1;
next[0] = j;
@@ -386,7 +386,7 @@ if (j == (t.size() - 1) ) {
那么使用next数组,用模式串匹配文本串的整体代码如下:
-```C++
+```CPP
int j = -1; // 因为next数组里记录的起始位置为-1
for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
@@ -405,7 +405,7 @@ for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
# 前缀表统一减一 C++代码实现
-```C++
+```CPP
class Solution {
public:
void getNext(int* next, const string& s) {
@@ -457,7 +457,7 @@ public:
我给出的getNext的实现为:(前缀表统一减一)
-```C++
+```CPP
void getNext(int* next, const string& s) {
int j = -1;
next[0] = j;
@@ -479,7 +479,7 @@ void getNext(int* next, const string& s) {
那么前缀表不减一来构建next数组,代码如下:
-```C++
+```CPP
void getNext(int* next, const string& s) {
int j = 0;
next[0] = 0;
@@ -502,7 +502,7 @@ void getNext(int* next, const string& s) {
实现代码如下:
-```C++
+```CPP
class Solution {
public:
void getNext(int* next, const string& s) {
@@ -902,4 +902,4 @@ var strStr = function (haystack, needle) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0031.下一个排列.md b/problems/0031.下一个排列.md
index fbe31eb3..05321a9a 100644
--- a/problems/0031.下一个排列.md
+++ b/problems/0031.下一个排列.md
@@ -11,7 +11,7 @@
# 31.下一个排列
-链接:https://leetcode-cn.com/problems/next-permutation/
+[力扣题目链接](https://leetcode-cn.com/problems/next-permutation/)
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
@@ -75,7 +75,7 @@
对应的C++代码如下:
-```C++
+```CPP
class Solution {
public:
void nextPermutation(vector& nums) {
@@ -99,6 +99,24 @@ public:
## Java
```java
+class Solution {
+ public void nextPermutation(int[] nums) {
+ for (int i = nums.length - 1; i >= 0; i--) {
+ for (int j = nums.length - 1; j > i; j--) {
+ if (nums[j] > nums[i]) {
+ // 交换
+ int temp = nums[i];
+ nums[i] = nums[j];
+ nums[j] = temp;
+ // [i + 1, nums.length) 内元素升序排序
+ Arrays.sort(nums, i + 1, nums.length);
+ return;
+ }
+ }
+ }
+ Arrays.sort(nums); // 不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
+ }
+}
```
## Python
@@ -120,5 +138,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md
index 97b9a9b6..68dd797d 100644
--- a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md
+++ b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md
@@ -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)
下面我来把所有情况都讨论一下。
@@ -50,21 +50,21 @@
接下来,在去寻找左边界,和右边界了。
-采用二分法来取寻找左右边界,为了让代码清晰,我分别写两个二分来寻找左边界和右边界。
+采用二分法来去寻找左右边界,为了让代码清晰,我分别写两个二分来寻找左边界和右边界。
**刚刚接触二分搜索的同学不建议上来就像如果用一个二分来查找左右边界,很容易把自己绕进去,建议扎扎实实的写两个二分分别找左边界和右边界**
## 寻找右边界
-先来寻找右边界,至于二分查找,如果看过[为什么每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。
+先来寻找右边界,至于二分查找,如果看过[704.二分查找](https://programmercarl.com/0704.二分查找.html)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。
-那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[为什么每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)这篇文章先看了,在把「leetcode:35.搜索插入位置」做了之后在做这道题目就好很多了)
+那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[704.二分查找](https://programmercarl.com/0704.二分查找.html)这篇文章先看了,704题目做了之后再做这道题目就好很多了)
确定好:计算出来的右边界是不包好target的右边界,左边界同理。
可以写出如下代码
-```C++
+```CPP
// 二分查找,寻找target的右边界(不包括target)
// 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2),为了处理情况一
int getRightBorder(vector& nums, int target) {
@@ -86,7 +86,7 @@ int getRightBorder(vector& nums, int target) {
## 寻找左边界
-```C++
+```CPP
// 二分查找,寻找target的左边界leftBorder(不包括target)
// 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4),为了处理情况一
int getLeftBorder(vector& nums, int target) {
@@ -110,7 +110,7 @@ int getLeftBorder(vector& nums, int target) {
左右边界计算完之后,看一下主体代码,这里把上面讨论的三种情况,都覆盖了
-```C++
+```CPP
class Solution {
public:
vector searchRange(vector& nums, int target) {
@@ -223,7 +223,7 @@ class Solution {
// 解法2
// 1、首先,在 nums 数组中二分查找 target;
// 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
-// 3、如果二分查找失败,则 binarySearch 返回 nums 中 为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
+// 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
class Solution {
public int[] searchRange(int[] nums, int target) {
@@ -275,6 +275,117 @@ class Solution {
## Python
```python
+class Solution:
+ def searchRange(self, nums: List[int], target: int) -> List[int]:
+ def getRightBorder(nums:List[int], target:int) -> int:
+ left, right = 0, len(nums)-1
+ rightBoder = -2 # 记录一下rightBorder没有被赋值的情况
+ while left <= right:
+ middle = left + (right-left) // 2
+ if nums[middle] > target:
+ right = middle - 1
+ else: # 寻找右边界,nums[middle] == target的时候更新left
+ left = middle + 1
+ rightBoder = left
+
+ return rightBoder
+
+ def getLeftBorder(nums:List[int], target:int) -> int:
+ left, right = 0, len(nums)-1
+ leftBoder = -2 # 记录一下leftBorder没有被赋值的情况
+ while left <= right:
+ middle = left + (right-left) // 2
+ if nums[middle] >= target: # 寻找左边界,nums[middle] == target的时候更新right
+ right = middle - 1;
+ leftBoder = right;
+ else:
+ left = middle + 1
+ return leftBoder
+ leftBoder = getLeftBorder(nums, target)
+ rightBoder = getRightBorder(nums, target)
+ # 情况一
+ if leftBoder == -2 or rightBoder == -2: return [-1, -1]
+ # 情况三
+ if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]
+ # 情况二
+ return [-1, -1]
+```
+```python
+# 解法2
+# 1、首先,在 nums 数组中二分查找 target;
+# 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
+# 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
+class Solution:
+ def searchRange(self, nums: List[int], target: int) -> List[int]:
+ def binarySearch(nums:List[int], target:int) -> int:
+ left, right = 0, len(nums)-1
+ while left<=right: # 不变量:左闭右闭区间
+ middle = left + (right-left) // 2
+ if nums[middle] > target:
+ right = middle - 1
+ elif nums[middle] < target:
+ left = middle + 1
+ else:
+ return middle
+ return -1
+ index = binarySearch(nums, target)
+ if index == -1:return [-1, -1] # nums 中不存在 target,直接返回 {-1, -1}
+ # nums 中存在 targe,则左右滑动指针,来找到符合题意的区间
+ left, right = index, index
+ # 向左滑动,找左边界
+ while left -1 >=0 and nums[left - 1] == target: left -=1
+ # 向右滑动,找右边界
+ while right+1 < len(nums) and nums[right + 1] == target: right +=1
+ return [left, right]
+```
+```python
+# 解法3
+# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标(左边界)与第一个大于target的下标(右边界);
+# 2、如果左边界<= 右边界,则返回 [左边界, 右边界]。否则返回[-1, -1]
+class Solution:
+ def searchRange(self, nums: List[int], target: int) -> List[int]:
+ def binarySearch(nums:List[int], target:int, lower:bool) -> int:
+ left, right = 0, len(nums)-1
+ ans = len(nums)
+ while left<=right: # 不变量:左闭右闭区间
+ middle = left + (right-left) //2
+ # lower为True,执行前半部分,找到第一个大于等于 target的下标 ,否则找到第一个大于target的下标
+ if nums[middle] > target or (lower and nums[middle] >= target):
+ right = middle - 1
+ ans = middle
+ else:
+ left = middle + 1
+ return ans
+
+ leftBorder = binarySearch(nums, target, True) # 搜索左边界
+ rightBorder = binarySearch(nums, target, False) -1 # 搜索右边界
+ if leftBorder<= rightBorder and rightBorder< len(nums) and nums[leftBorder] == target and nums[rightBorder] == target:
+ return [leftBorder, rightBorder]
+ return [-1, -1]
+```
+
+```python
+# 解法4
+# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标leftBorder;
+# 2、在 nums 数组中二分查找得到第一个大于等于 target+1的下标, 减1则得到rightBorder;
+# 3、如果开始位置在数组的右边或者不存在target,则返回[-1, -1] 。否则返回[leftBorder, rightBorder]
+class Solution:
+ def searchRange(self, nums: List[int], target: int) -> List[int]:
+ def binarySearch(nums:List[int], target:int) -> int:
+ left, right = 0, len(nums)-1
+ while left<=right: # 不变量:左闭右闭区间
+ middle = left + (right-left) //2
+ if nums[middle] >= target:
+ right = middle - 1
+ else:
+ left = middle + 1
+ return left # 若存在target,则返回第一个等于target的值
+
+ leftBorder = binarySearch(nums, target) # 搜索左边界
+ rightBorder = binarySearch(nums, target+1) -1 # 搜索右边界
+ if leftBorder == len(nums) or nums[leftBorder]!= target: # 情况一和情况二
+ return [-1, -1]
+ return [leftBorder, rightBorder]
```
## Go
@@ -291,5 +402,5 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md
index 1c5ad2c8..a536f0ec 100644
--- a/problems/0035.搜索插入位置.md
+++ b/problems/0035.搜索插入位置.md
@@ -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)
效率如下:
@@ -116,7 +116,7 @@ public:
**大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1**。
-```C++
+```CPP
class Solution {
public:
int searchInsert(vector& nums, int target) {
@@ -158,7 +158,7 @@ public:
**大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = middle**。
-```C++
+```CPP
class Solution {
public:
int searchInsert(vector& nums, int target) {
@@ -234,29 +234,24 @@ class Solution {
```
-
-
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
```
-
-Go:
-
JavaScript:
```js
var searchInsert = function (nums, target) {
@@ -277,9 +272,45 @@ var searchInsert = function (nums, target) {
};
```
+Swift:
+
+```swift
+// 暴力法
+func searchInsert(_ nums: [Int], _ target: Int) -> Int {
+ for i in 0..= target {
+ return i
+ }
+ }
+ return nums.count
+}
+
+// 二分法
+func searchInsert(_ nums: [Int], _ target: Int) -> Int {
+ var left = 0
+ var right = nums.count - 1
+
+ while left <= right {
+ let middle = left + ((right - left) >> 1)
+
+ if nums[middle] > target {
+ right = middle - 1
+ }else if nums[middle] < target {
+ left = middle + 1
+ }else if nums[middle] == target {
+ return middle
+ }
+ }
+
+ return right + 1
+}
+```
+
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0037.解数独.md b/problems/0037.解数独.md
index e43708b8..b6fa0d6e 100644
--- a/problems/0037.解数独.md
+++ b/problems/0037.解数独.md
@@ -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)中已经介绍过了,一样的道理。
代码如下:
@@ -90,7 +90,7 @@ bool backtracking(vector>& board)
代码如下:(**详细看注释**)
-```C++
+```CPP
bool backtracking(vector>& board) {
for (int i = 0; i < board.size(); i++) { // 遍历行
for (int j = 0; j < board[0].size(); j++) { // 遍历列
@@ -125,7 +125,7 @@ bool backtracking(vector>& board) {
代码如下:
-```C++
+```CPP
bool isValid(int row, int col, char val, vector>& board) {
for (int i = 0; i < 9; i++) { // 判断行里是否重复
if (board[row][i] == val) {
@@ -154,7 +154,7 @@ bool isValid(int row, int col, char val, vector>& board) {
## C++代码
-```C++
+```CPP
class Solution {
private:
bool backtracking(vector>& board) {
@@ -437,4 +437,4 @@ var solveSudoku = function(board) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md
index ba8128b5..97987e6d 100644
--- a/problems/0039.组合总和.md
+++ b/problems/0039.组合总和.md
@@ -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,15 +65,15 @@ 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)
**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我再讲解排列的时候就重点介绍**。
代码如下:
-```C++
+```CPP
vector> result;
vector path;
void backtracking(vector& candidates, int target, int sum, int startIndex)
@@ -89,7 +89,7 @@ void backtracking(vector& candidates, int target, int sum, int startIndex)
sum等于target的时候,需要收集结果,代码如下:
-```C++
+```CPP
if (sum > target) {
return;
}
@@ -103,11 +103,11 @@ 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)的一个区别是:本题元素为可重复选取的**。
如何重复选取呢,看代码,注释部分:
-```C++
+```CPP
for (int i = startIndex; i < candidates.size(); i++) {
sum += candidates[i];
path.push_back(candidates[i]);
@@ -117,9 +117,9 @@ for (int i = startIndex; i < candidates.size(); i++) {
}
```
-按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中给出的模板,不难写出如下C++完整代码:
+按照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中给出的模板,不难写出如下C++完整代码:
-```C++
+```CPP
// 版本一
class Solution {
private:
@@ -179,7 +179,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
整体代码如下:(注意注释的部分)
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -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)做了对比。
最后还给出了本题的剪枝优化,这个优化如果是初学者的话并不容易想到。
@@ -346,9 +346,63 @@ var combinationSum = function(candidates, target) {
};
```
+C:
+```c
+int* path;
+int pathTop;
+int** ans;
+int ansTop;
+//记录每一个和等于target的path数组长度
+int* length;
+
+void backTracking(int target, int index, int* candidates, int candidatesSize, int sum) {
+ //若sum>=target就应该终止遍历
+ if(sum >= target) {
+ //若sum等于target,将当前的组合放入ans数组中
+ if(sum == target) {
+ int* tempPath = (int*)malloc(sizeof(int) * pathTop);
+ int j;
+ for(j = 0; j < pathTop; j++) {
+ tempPath[j] = path[j];
+ }
+ ans[ansTop] = tempPath;
+ length[ansTop++] = pathTop;
+ }
+ return ;
+ }
+
+ int i;
+ for(i = index; i < candidatesSize; i++) {
+ //将当前数字大小加入sum
+ sum+=candidates[i];
+ path[pathTop++] = candidates[i];
+ backTracking(target, i, candidates, candidatesSize, sum);
+ sum-=candidates[i];
+ pathTop--;
+ }
+}
+
+int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){
+ //初始化变量
+ path = (int*)malloc(sizeof(int) * 50);
+ ans = (int**)malloc(sizeof(int*) * 200);
+ length = (int*)malloc(sizeof(int) * 200);
+ ansTop = pathTop = 0;
+ backTracking(target, 0, candidates, candidatesSize, 0);
+
+ //设置返回的数组大小
+ *returnSize = ansTop;
+ *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
+ int i;
+ for(i = 0; i < ansTop; i++) {
+ (*returnColumnSizes)[i] = length[i];
+ }
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md
index 70e012da..8925ef81 100644
--- a/problems/0040.组合总和II.md
+++ b/problems/0040.组合总和II.md
@@ -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)有重复元素,但还不能有重复的组合**。
@@ -63,7 +63,7 @@ candidates 中的每个数字在每个组合中只能使用一次。
都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。**没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。**
-那么问题来了,我们是要同一树层上使用过,还是统一树枝上使用过呢?
+那么问题来了,我们是要同一树层上使用过,还是同一树枝上使用过呢?
回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。
@@ -84,13 +84,13 @@ candidates 中的每个数字在每个组合中只能使用一次。
* **递归函数参数**
-与[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
+与[39.组合总和](https://programmercarl.com/0039.组合总和.html)套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
这个集合去重的重任就是used来完成的。
代码如下:
-```C++
+```CPP
vector> result; // 存放组合集合
vector path; // 符合条件的组合
void backtracking(vector& candidates, int target, int sum, int startIndex, vector& used) {
@@ -98,11 +98,11 @@ void backtracking(vector& 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`。
代码如下:
-```C++
+```CPP
if (sum > target) { // 这个条件其实可以省略
return;
}
@@ -116,7 +116,7 @@ if (sum == target) {
* **单层搜索的逻辑**
-这里与[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)最大的不同就是要去重了。
+这里与[39.组合总和](https://programmercarl.com/0039.组合总和.html)最大的不同就是要去重了。
前面我们提到:要去重的是“同一树层上的使用过”,如果判断同一树层上元素(相同的元素)是否使用过了呢。
@@ -137,7 +137,7 @@ if (sum == target) {
那么单层搜索的逻辑代码如下:
-```C++
+```CPP
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
// used[i - 1] == true,说明同一树支candidates[i - 1]使用过
// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
@@ -161,7 +161,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
回溯三部曲分析完了,整体C++代码如下:
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -206,7 +206,7 @@ public:
这里直接用startIndex来去重也是可以的, 就不用used数组了。
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -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 = []
@@ -392,10 +392,69 @@ var combinationSum2 = function(candidates, target) {
}
};
```
+C:
+```c
+int* path;
+int pathTop;
+int** ans;
+int ansTop;
+//记录ans中每一个一维数组的大小
+int* length;
+int cmp(const void* a1, const void* a2) {
+ return *((int*)a1) - *((int*)a2);
+}
+void backTracking(int* candidates, int candidatesSize, int target, int sum, int startIndex) {
+ if(sum >= target) {
+ //若sum等于target,复制当前path进入
+ if(sum == target) {
+ int* tempPath = (int*)malloc(sizeof(int) * pathTop);
+ int j;
+ for(j = 0; j < pathTop; j++) {
+ tempPath[j] = path[j];
+ }
+ length[ansTop] = pathTop;
+ ans[ansTop++] = tempPath;
+ }
+ return ;
+ }
+
+ int i;
+ for(i = startIndex; i < candidatesSize; i++) {
+ //对同一层树中使用过的元素跳过
+ if(i > startIndex && candidates[i] == candidates[i-1])
+ continue;
+ path[pathTop++] = candidates[i];
+ sum += candidates[i];
+ backTracking(candidates, candidatesSize, target, sum, i + 1);
+ //回溯
+ sum -= candidates[i];
+ pathTop--;
+ }
+}
+
+int** combinationSum2(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){
+ path = (int*)malloc(sizeof(int) * 50);
+ ans = (int**)malloc(sizeof(int*) * 100);
+ length = (int*)malloc(sizeof(int) * 100);
+ pathTop = ansTop = 0;
+ //快速排序candidates,让相同元素挨到一起
+ qsort(candidates, candidatesSize, sizeof(int), cmp);
+
+ backTracking(candidates, candidatesSize, target, 0, 0);
+
+ *returnSize = ansTop;
+ *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
+ int i;
+ for(i = 0; i < ansTop; i++) {
+ (*returnColumnSizes)[i] = length[i];
+ }
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0042.接雨水.md b/problems/0042.接雨水.md
index 3f4d0123..4383a0b8 100644
--- a/problems/0042.接雨水.md
+++ b/problems/0042.接雨水.md
@@ -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
-
+
# 思路
@@ -77,7 +77,7 @@
一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。
首先从头遍历所有的列,并且**要注意第一个柱子和最后一个柱子不接雨水**,代码如下:
-```C++
+```CPP
for (int i = 0; i < height.size(); i++) {
// 第一个柱子和最后一个柱子不接雨水
if (i == 0 || i == height.size() - 1) continue;
@@ -86,7 +86,7 @@ for (int i = 0; i < height.size(); i++) {
在for循环中求左右两边最高柱子,代码如下:
-```C++
+```CPP
int rHeight = height[i]; // 记录右边柱子的最高高度
int lHeight = height[i]; // 记录左边柱子的最高高度
for (int r = i + 1; r < height.size(); r++) {
@@ -99,14 +99,14 @@ for (int l = i - 1; l >= 0; l--) {
最后,计算该列的雨水高度,代码如下:
-```C++
+```CPP
int h = min(lHeight, rHeight) - height[i];
if (h > 0) sum += h; // 注意只有h大于零的时候,在统计到总和中
```
整体代码如下:
-```C++
+```CPP
class Solution {
public:
int trap(vector& height) {
@@ -152,7 +152,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
int trap(vector& height) {
@@ -186,7 +186,7 @@ public:
这个解法可以说是最不好理解的了,所以下面我花了大量的篇幅来介绍这种方法。
-单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://mp.weixin.qq.com/s/Xgcqx5eBa3xZabt_LurnNQ)一样,需要我们自己维持顺序,没有现成的容器可以用。
+单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://programmercarl.com/0239.滑动窗口最大值.html)一样,需要我们自己维持顺序,没有现成的容器可以用。
### 准备工作
@@ -287,7 +287,7 @@ if (height[i] == height[st.top()]) { // 例如 5 5 1 7 这种情况
求当前凹槽雨水的体积代码如下:
-```C++
+```CPP
while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,持续跟新栈顶元素
int mid = st.top();
st.pop();
@@ -301,7 +301,7 @@ while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,
关键部分讲完了,整体代码如下:
-```C++
+```CPP
class Solution {
public:
int trap(vector& height) {
@@ -335,7 +335,7 @@ public:
以上代码冗余了一些,但是思路是清晰的,下面我将代码精简一下,如下:
-```C++
+```CPP
class Solution {
public:
int trap(vector& height) {
@@ -366,6 +366,107 @@ 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;
+ }
+}
+```
+
+单调栈法
+```java
+class Solution {
+ public int trap(int[] height){
+ int size = height.length;
+
+ if (size <= 2) return 0;
+
+ // in the stack, we push the index of array
+ // using height[] to access the real height
+ Stack stack = new Stack();
+ stack.push(0);
+
+ int sum = 0;
+ for (int index = 1; index < size; index++){
+ int stackTop = stack.peek();
+ if (height[index] < height[stackTop]){
+ stack.push(index);
+ }else if (height[index] == height[stackTop]){
+ // 因为相等的相邻墙,左边一个是不可能存放雨水的,所以pop左边的index, push当前的index
+ stack.pop();
+ stack.push(index);
+ }else{
+ //pop up all lower value
+ int heightAtIdx = height[index];
+ while (!stack.isEmpty() && (heightAtIdx > height[stackTop])){
+ int mid = stack.pop();
+
+ if (!stack.isEmpty()){
+ int left = stack.peek();
+
+ int h = Math.min(height[left], height[index]) - height[mid];
+ int w = index - left - 1;
+ int hold = h * w;
+ if (hold > 0) sum += hold;
+ stackTop = stack.peek();
+ }
+ }
+ stack.push(index);
+ }
+ }
+
+ return sum;
+ }
+}
+```
+
Python:
双指针法
@@ -388,6 +489,44 @@ class Solution:
res += res1
return res
```
+动态规划
+```python3
+class Solution:
+ def trap(self, height: List[int]) -> int:
+ leftheight, rightheight = [0]*len(height), [0]*len(height)
+
+ leftheight[0]=height[0]
+ for i in range(1,len(height)):
+ leftheight[i]=max(leftheight[i-1],height[i])
+ rightheight[-1]=height[-1]
+ for i in range(len(height)-2,-1,-1):
+ rightheight[i]=max(rightheight[i+1],height[i])
+
+ result = 0
+ for i in range(0,len(height)):
+ summ = min(leftheight[i],rightheight[i])-height[i]
+ result += summ
+ return result
+```
+单调栈
+```python3
+class Solution:
+ def trap(self, height: List[int]) -> int:
+ st =[0]
+ result = 0
+ for i in range(1,len(height)):
+ while st!=[] and height[i]>height[st[-1]]:
+ midh = height[st[-1]]
+ st.pop()
+ if st!=[]:
+ hright = height[i]
+ hleft = height[st[-1]]
+ h = min(hright,hleft)-midh
+ w = i-st[-1]-1
+ result+=h*w
+ st.append(i)
+ return result
+```
Go:
@@ -398,4 +537,4 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md
index a161d944..8dd59838 100644
--- a/problems/0045.跳跃游戏II.md
+++ b/problems/0045.跳跃游戏II.md
@@ -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)还是难了不少。
但思路是相似的,还是要看最大覆盖范围。
@@ -63,7 +63,7 @@
C++代码如下:(详细注释)
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -106,7 +106,7 @@ public:
代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -132,7 +132,7 @@ public:
## 总结
-相信大家可以发现,这道题目相当于[55.跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不止一点。
+相信大家可以发现,这道题目相当于[55.跳跃游戏](https://programmercarl.com/0055.跳跃游戏.html)难了不止一点。
但代码又十分简单,贪心就是这么巧妙。
@@ -236,4 +236,4 @@ var jump = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0046.全排列.md b/problems/0046.全排列.md
index 3ee7271e..001c249e 100644
--- a/problems/0046.全排列.md
+++ b/problems/0046.全排列.md
@@ -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。
@@ -106,7 +106,7 @@ for (int i = 0; i < nums.size(); i++) {
整体C++代码如下:
-```C++
+```CPP
class Solution {
public:
vector> result;
@@ -183,6 +183,32 @@ class Solution {
}
}
```
+```java
+// 解法2:通过判断path中是否存在数字,排除已经选择的数字
+class Solution {
+ List> result = new ArrayList<>();
+ LinkedList path = new LinkedList<>();
+ public List> permute(int[] nums) {
+ if (nums.length == 0) return result;
+ backtrack(nums, path);
+ return result;
+ }
+ public void backtrack(int[] nums, LinkedList path) {
+ if (path.size() == nums.length) {
+ result.add(new ArrayList<>(path));
+ }
+ for (int i =0; i < nums.length; i++) {
+ // 如果path中已有,则跳过
+ if (path.contains(nums[i])) {
+ continue;
+ }
+ path.add(nums[i]);
+ backtrack(nums, path);
+ path.removeLast();
+ }
+ }
+}
+```
Python:
```python3
@@ -227,24 +253,27 @@ class Solution:
Go:
```Go
-var result [][]int
-func backtrack(nums,pathNums []int,used []bool){
- if len(nums)==len(pathNums){
- tmp:=make([]int,len(nums))
- copy(tmp,pathNums)
- result=append(result,tmp)
- //result=append(result,pathNums)
- return
- }
- for i:=0;i
+
diff --git a/problems/0047.全排列II.md b/problems/0047.全排列II.md
index 66107c95..01706eb3 100644
--- a/problems/0047.全排列II.md
+++ b/problems/0047.全排列II.md
@@ -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++代码
@@ -228,35 +228,31 @@ Go:
```go
var res [][]int
func permute(nums []int) [][]int {
- res = [][]int{}
- sort.Ints(nums)
- dfs(nums, make([]int, 0), make([]bool, len(nums)))
- return res
+ res = [][]int{}
+ backTrack(nums,len(nums),[]int{})
+ return res
}
+func backTrack(nums []int,numsLen int,path []int) {
+ if len(nums)==0{
+ p:=make([]int,len(path))
+ copy(p,path)
+ res = append(res,p)
+ }
+ used := [21]int{}//跟前一题唯一的区别,同一层不使用重复的数。关于used的思想carl在递增子序列那一题中提到过
+ for i:=0;i
+
diff --git a/problems/0051.N皇后.md b/problems/0051.N皇后.md
index a8919ec3..69802ecb 100644
--- a/problems/0051.N皇后.md
+++ b/problems/0051.N皇后.md
@@ -9,7 +9,7 @@
## 第51题. N皇后
-题目链接: https://leetcode-cn.com/problems/n-queens/
+[力扣题目链接](https://leetcode-cn.com/problems/n-queens/)
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
@@ -21,18 +21,27 @@ n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
+
输入: 4
-输出: [
- [".Q..", // 解法 1
+
+输出:
+
+解法 1
+
+[
+ [".Q..",
"...Q",
"Q...",
"..Q."],
- ["..Q.", // 解法 2
+解法 2
+
+ ["..Q.",
"Q...",
"...Q",
".Q.."]
]
+
解释: 4 皇后问题存在两个不同的解法。
提示:
@@ -171,7 +180,7 @@ bool isValid(int row, int col, vector& chessboard, int n) {
## C++代码
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -332,7 +341,7 @@ class Solution {
public boolean isValid(int row, int col, int n, char[][] chessboard) {
// 检查列
- for (int i=0; i
+
diff --git a/problems/0052.N皇后II.md b/problems/0052.N皇后II.md
new file mode 100644
index 00000000..1a34f763
--- /dev/null
+++ b/problems/0052.N皇后II.md
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
+
+# 52. N皇后II
+
+题目链接:https://leetcode-cn.com/problems/n-queens-ii/
+
+n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
+
+上图为 8 皇后问题的一种解法。
+
+
+给定一个整数 n,返回 n 皇后不同的解决方案的数量。
+
+示例:
+
+输入: 4
+
+输出: 2
+
+解释: 4 皇后问题存在如下两个不同的解法。
+
+解法 1
+
+[
+ [".Q..",
+ "...Q",
+ "Q...",
+ "..Q."],
+
+解法 2
+
+ ["..Q.",
+ "Q...",
+ "...Q",
+ ".Q.."]
+]
+
+# 思路
+
+
+想看:[51.N皇后](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg) ,基本没有区别
+
+# C++代码
+
+```CPP
+class Solution {
+private:
+int count = 0;
+void backtracking(int n, int row, vector& chessboard) {
+ if (row == n) {
+ count++;
+ return;
+ }
+ for (int col = 0; col < n; col++) {
+ if (isValid(row, col, chessboard, n)) {
+ chessboard[row][col] = 'Q'; // 放置皇后
+ backtracking(n, row + 1, chessboard);
+ chessboard[row][col] = '.'; // 回溯
+ }
+ }
+}
+bool isValid(int row, int col, vector& chessboard, int n) {
+ int count = 0;
+ // 检查列
+ for (int i = 0; i < row; i++) { // 这是一个剪枝
+ if (chessboard[i][col] == 'Q') {
+ return false;
+ }
+ }
+ // 检查 45度角是否有皇后
+ for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
+ if (chessboard[i][j] == 'Q') {
+ return false;
+ }
+ }
+ // 检查 135度角是否有皇后
+ for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
+ if (chessboard[i][j] == 'Q') {
+ return false;
+ }
+ }
+ return true;
+}
+
+public:
+ int totalNQueens(int n) {
+ std::vector chessboard(n, std::string(n, '.'));
+ backtracking(n, 0, chessboard);
+ return count;
+
+ }
+};
+```
+
+# 其他语言补充
+
diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md
index 81e0b35a..53159978 100644
--- a/problems/0053.最大子序和.md
+++ b/problems/0053.最大子序和.md
@@ -9,7 +9,7 @@
## 53. 最大子序和
-题目地址:https://leetcode-cn.com/problems/maximum-subarray/
+[力扣题目链接](https://leetcode-cn.com/problems/maximum-subarray/)
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
@@ -25,7 +25,7 @@
时间复杂度:O(n^2)
空间复杂度:O(1)
-```C++
+```CPP
class Solution {
public:
int maxSubArray(vector& nums) {
@@ -81,7 +81,7 @@ if (count > result) result = count;
那么不难写出如下C++代码(关键地方已经注释)
-```C++
+```CPP
class Solution {
public:
int maxSubArray(vector& nums) {
@@ -109,7 +109,7 @@ public:
那么先给出我的dp代码如下,有时间的录友可以提前做一做:
-```C++
+```CPP
class Solution {
public:
int maxSubArray(vector& nums) {
@@ -214,4 +214,4 @@ var maxSubArray = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0053.最大子序和(动态规划).md b/problems/0053.最大子序和(动态规划).md
index dd0e513b..1efd7e12 100644
--- a/problems/0053.最大子序和(动态规划).md
+++ b/problems/0053.最大子序和(动态规划).md
@@ -8,7 +8,7 @@
## 53. 最大子序和
-题目地址:https://leetcode-cn.com/problems/maximum-subarray/
+[力扣题目链接](https://leetcode-cn.com/problems/maximum-subarray/)
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
@@ -19,7 +19,7 @@
## 思路
-这道题之前我们在讲解贪心专题的时候用贪心算法解决过一次,[贪心算法:最大子序和](https://mp.weixin.qq.com/s/DrjIQy6ouKbpletQr0g1Fg)。
+这道题之前我们在讲解贪心专题的时候用贪心算法解决过一次,[贪心算法:最大子序和](https://programmercarl.com/0053.最大子序和.html)。
这次我们用动态规划的思路再来分析一次。
@@ -65,7 +65,7 @@ dp[0]应该是多少呢?
以上动规五部曲分析完毕,完整代码如下:
-```C++
+```CPP
class Solution {
public:
int maxSubArray(vector& nums) {
@@ -87,7 +87,7 @@ public:
## 总结
-这道题目用贪心也很巧妙,但有一点绕,需要仔细想一想,如果想回顾一下贪心就看这里吧:[贪心算法:最大子序和](https://mp.weixin.qq.com/s/DrjIQy6ouKbpletQr0g1Fg)
+这道题目用贪心也很巧妙,但有一点绕,需要仔细想一想,如果想回顾一下贪心就看这里吧:[贪心算法:最大子序和](https://programmercarl.com/0053.最大子序和.html)
动规的解法还是很直接的。
@@ -138,7 +138,52 @@ class Solution:
```
Go:
+```Go
+// solution
+// 1, dp
+// 2, 贪心
+func maxSubArray(nums []int) int {
+ n := len(nums)
+ // 这里的dp[i] 表示,最大的连续子数组和,包含num[i] 元素
+ dp := make([]int,n)
+ // 初始化,由于dp 状态转移方程依赖dp[0]
+ dp[0] = nums[0]
+ // 初始化最大的和
+ mx := nums[0]
+ for i:=1;ib {
+ return a
+ }
+ return b
+}
+```
+
+JavaScript:
+
+```javascript
+const maxSubArray = nums => {
+ // 数组长度,dp初始化
+ const [len, dp] = [nums.length, [nums[0]]];
+ // 最大值初始化为dp[0]
+ let max = dp[0];
+ for (let i = 1; i < len; i++) {
+ dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
+ // 更新最大值
+ max = Math.max(max, dp[i]);
+ }
+ return max;
+};
+```
@@ -146,4 +191,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0055.跳跃游戏.md b/problems/0055.跳跃游戏.md
index 8618515e..c9ebe0fe 100644
--- a/problems/0055.跳跃游戏.md
+++ b/problems/0055.跳跃游戏.md
@@ -9,7 +9,7 @@
## 55. 跳跃游戏
-题目链接:https://leetcode-cn.com/problems/jump-game/
+[力扣题目链接](https://leetcode-cn.com/problems/jump-game/)
给定一个非负整数数组,你最初位于数组的第一个位置。
@@ -58,7 +58,7 @@ i每次移动只能在cover的范围内移动,每移动一个元素,cover得
C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool canJump(vector& nums) {
@@ -161,4 +161,4 @@ var canJump = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0056.合并区间.md b/problems/0056.合并区间.md
index d4ffc554..82ca29e6 100644
--- a/problems/0056.合并区间.md
+++ b/problems/0056.合并区间.md
@@ -9,7 +9,7 @@
## 56. 合并区间
-题目链接:https://leetcode-cn.com/problems/merge-intervals/
+[力扣题目链接](https://leetcode-cn.com/problems/merge-intervals/)
给出一个区间的集合,请合并所有重叠的区间。
@@ -56,7 +56,7 @@
C++代码如下:
-```C++
+```CPP
class Solution {
public:
// 按照区间左边界从小到大排序
@@ -92,7 +92,7 @@ public:
当然以上代码有冗余一些,可以优化一下,如下:(思路是一样的)
-```C++
+```CPP
class Solution {
public:
vector> merge(vector>& intervals) {
@@ -126,7 +126,7 @@ public:
那应该怎么办呢?
-正如我贪心系列开篇词[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/O935TaoHE9Eexwe_vSbRAg)中讲解的一样,贪心本来就没有套路,也没有框架,所以各种常规解法需要多接触多练习,自然而然才会想到。
+正如我贪心系列开篇词[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html)中讲解的一样,贪心本来就没有套路,也没有框架,所以各种常规解法需要多接触多练习,自然而然才会想到。
「代码随想录」会把贪心常见的经典题目覆盖到,大家只要认真学习打卡就可以了。
@@ -157,6 +157,28 @@ class Solution {
}
}
```
+```java
+// 版本2
+class Solution {
+ public int[][] merge(int[][] intervals) {
+ LinkedList res = new LinkedList<>();
+ Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));
+ res.add(intervals[0]);
+ for (int i = 1; i < intervals.length; i++) {
+ if (intervals[i][0] <= res.getLast()[1]) {
+ int start = res.getLast()[0];
+ int end = Math.max(intervals[i][1], res.getLast()[1]);
+ res.removeLast();
+ res.add(new int[]{start, end});
+ }
+ else {
+ res.add(intervals[i]);
+ }
+ }
+ return res.toArray(new int[res.size()][]);
+ }
+}
+```
Python:
```python
@@ -176,30 +198,27 @@ class Solution:
```
Go:
-```Go
+```golang
func merge(intervals [][]int) [][]int {
- sort.Slice(intervals, func(i, j int) bool {
- return intervals[i][0]=intervals[i+1][0]{
+ intervals[i][1]=max(intervals[i][1],intervals[i+1][1])//赋值最大值
+ intervals=append(intervals[:i+1],intervals[i+2:]...)
+ i--
+ }
+ }
+ return intervals
}
-func max(a, b int) int {
- if a > b { return a }
- return b
+func max(a,b int)int{
+ if a>b{
+ return a
+ }
+ return b
}
```
@@ -229,4 +248,4 @@ var merge = function (intervals) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0059.螺旋矩阵II.md b/problems/0059.螺旋矩阵II.md
index 90b74005..9a7dc850 100644
--- a/problems/0059.螺旋矩阵II.md
+++ b/problems/0059.螺旋矩阵II.md
@@ -10,8 +10,9 @@
## 59.螺旋矩阵II
-题目地址:https://leetcode-cn.com/problems/spiral-matrix-ii/
-给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
+[力扣题目链接](https://leetcode-cn.com/problems/spiral-matrix-ii/)
+
+给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
@@ -33,7 +34,7 @@
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里哪里有问题,改了那里这里又跑不起来了。
-大家还记得我们在这篇文章[数组:每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)中讲解了二分法,提到如果要写出正确的二分法一定要坚持**循环不变量原则**。
+大家还记得我们在这篇文章[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0704.二分查找.html)中讲解了二分法,提到如果要写出正确的二分法一定要坚持**循环不变量原则**。
而求解本题依然是要坚持循环不变量原则。
@@ -66,7 +67,7 @@
整体C++代码如下:
-```C++
+```CPP
class Solution {
public:
vector> generateMatrix(int n) {
@@ -191,33 +192,48 @@ class Solution {
python:
-```python
+```python3
class Solution:
+
def generateMatrix(self, n: int) -> List[List[int]]:
- left, right, up, down = 0, n-1, 0, n-1
- matrix = [ [0]*n for _ in range(n)]
- num = 1
- while left<=right and up<=down:
- # 填充左到右
- for i in range(left, right+1):
- matrix[up][i] = num
- num += 1
- up += 1
- # 填充上到下
- for i in range(up, down+1):
- matrix[i][right] = num
- num += 1
- right -= 1
- # 填充右到左
- for i in range(right, left-1, -1):
- matrix[down][i] = num
- num += 1
- down -= 1
- # 填充下到上
- for i in range(down, up-1, -1):
- matrix[i][left] = num
- num += 1
+ # 初始化要填充的正方形
+ matrix = [[0] * n for _ in range(n)]
+
+ left, right, up, down = 0, n - 1, 0, n - 1
+ number = 1 # 要填充的数字
+
+ while left < right and up < down:
+
+ # 从左到右填充上边
+ for x in range(left, right):
+ matrix[up][x] = number
+ number += 1
+
+ # 从上到下填充右边
+ for y in range(up, down):
+ matrix[y][right] = number
+ number += 1
+
+ # 从右到左填充下边
+ for x in range(right, left, -1):
+ matrix[down][x] = number
+ number += 1
+
+ # 从下到上填充左边
+ for y in range(down, up, -1):
+ matrix[y][left] = number
+ number += 1
+
+ # 缩小要填充的范围
left += 1
+ right -= 1
+ up += 1
+ down -= 1
+
+ # 如果阶数为奇数,额外填充一次中心
+ if n % 2:
+ matrix[n // 2][n // 2] = number
+
return matrix
```
@@ -302,11 +318,219 @@ 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
+}
+```
+
+Rust:
+
+```rust
+impl Solution {
+ pub fn generate_matrix(n: i32) -> Vec> {
+ let mut res = vec![vec![0; n as usize]; n as usize];
+ let (mut startX, mut startY, mut offset): (usize, usize, usize) = (0, 0, 1);
+ let mut loopIdx = n/2;
+ let mid: usize = loopIdx as usize;
+ let mut count = 1;
+ let (mut i, mut j): (usize, usize) = (0, 0);
+ while loopIdx > 0 {
+ i = startX;
+ j = startY;
+
+ while j < (startY + (n as usize) - offset) {
+ res[i][j] = count;
+ count += 1;
+ j += 1;
+ }
+
+ while i < (startX + (n as usize) - offset) {
+ res[i][j] = count;
+ count += 1;
+ i += 1;
+ }
+
+ while j > startY {
+ res[i][j] = count;
+ count += 1;
+ j -= 1;
+ }
+
+ while i > startX {
+ res[i][j] = count;
+ count += 1;
+ i -= 1;
+ }
+
+ startX += 1;
+ startY += 1;
+ offset += 2;
+ loopIdx -= 1;
+ }
+
+ if(n % 2 == 1) {
+ res[mid][mid] = count;
+ }
+ res
+ }
+}
+```
+
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer $n
+ * @return Integer[][]
+ */
+ function generateMatrix($n) {
+ // 初始化数组
+ $res = array_fill(0, $n, array_fill(0, $n, 0));
+ $mid = $loop = floor($n / 2);
+ $startX = $startY = 0;
+ $offset = 1;
+ $count = 1;
+ while ($loop > 0) {
+ $i = $startX;
+ $j = $startY;
+ for (; $j < $startY + $n - $offset; $j++) {
+ $res[$i][$j] = $count++;
+ }
+ for (; $i < $startX + $n - $offset; $i++) {
+ $res[$i][$j] = $count++;
+ }
+ for (; $j > $startY; $j--) {
+ $res[$i][$j] = $count++;
+ }
+ for (; $i > $startX; $i--) {
+ $res[$i][$j] = $count++;
+ }
+ $startX += 1;
+ $startY += 1;
+ $offset += 2;
+ $loop--;
+ }
+ if ($n % 2 == 1) {
+ $res[$mid][$mid] = $count;
+ }
+ return $res;
+ }
+}
+```
+
+C:
+```c
+int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
+ //初始化返回的结果数组的大小
+ *returnSize = n;
+ *returnColumnSizes = (int*)malloc(sizeof(int) * n);
+ //初始化返回结果数组ans
+ int** ans = (int**)malloc(sizeof(int*) * n);
+ int i;
+ for(i = 0; i < n; i++) {
+ ans[i] = (int*)malloc(sizeof(int) * n);
+ (*returnColumnSizes)[i] = n;
+ }
+
+ //设置每次循环的起始位置
+ int startX = 0;
+ int startY = 0;
+ //设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
+ int mid = n / 2;
+ //循环圈数
+ int loop = n / 2;
+ //偏移数
+ int offset = 1;
+ //当前要添加的元素
+ int count = 1;
+
+ while(loop) {
+ int i = startX;
+ int j = startY;
+ //模拟上侧从左到右
+ for(; j < startY + n - offset; j++) {
+ ans[startX][j] = count++;
+ }
+ //模拟右侧从上到下
+ for(; i < startX + n - offset; i++) {
+ ans[i][j] = count++;
+ }
+ //模拟下侧从右到左
+ for(; j > startY; j--) {
+ ans[i][j] = count++;
+ }
+ //模拟左侧从下到上
+ for(; i > startX; i--) {
+ ans[i][j] = count++;
+ }
+ //偏移值每次加2
+ offset+=2;
+ //遍历起始位置每次+1
+ startX++;
+ startY++;
+ loop--;
+ }
+ //若n为奇数需要单独给矩阵中间赋值
+ if(n%2)
+ ans[mid][mid] = count;
+
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md
index 50e70b3e..ce29cca1 100644
--- a/problems/0062.不同路径.md
+++ b/problems/0062.不同路径.md
@@ -8,7 +8,7 @@
## 62.不同路径
-题目链接:https://leetcode-cn.com/problems/unique-paths/
+[力扣题目链接](https://leetcode-cn.com/problems/unique-paths/)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
@@ -40,7 +40,7 @@
示例 4:
输入:m = 3, n = 3
输出:6
-
+
提示:
* 1 <= m, n <= 100
* 题目数据保证答案小于等于 2 * 10^9
@@ -59,7 +59,7 @@
此时问题就可以转化为求二叉树叶子节点的个数,代码如下:
-```C++
+```CPP
class Solution {
private:
int dfs(int i, int j, int m, int n) {
@@ -128,7 +128,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1;
以上动规五部曲分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int uniquePaths(int m, int n) {
@@ -149,7 +149,7 @@ public:
其实用一个一维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了二维,在理解一维,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int uniquePaths(int m, int n) {
@@ -187,7 +187,7 @@ public:
例如如下代码是不行的。
-```C++
+```CPP
class Solution {
public:
int uniquePaths(int m, int n) {
@@ -204,7 +204,7 @@ public:
需要在计算分子的时候,不断除以分母,代码如下:
-```C++
+```CPP
class Solution {
public:
int uniquePaths(int m, int n) {
@@ -333,4 +333,4 @@ var uniquePaths = function(m, n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md
index a61ffd02..c01846bd 100644
--- a/problems/0063.不同路径II.md
+++ b/problems/0063.不同路径II.md
@@ -8,7 +8,7 @@
## 63. 不同路径 II
-题目链接:https://leetcode-cn.com/problems/unique-paths-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/unique-paths-ii/)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
@@ -49,11 +49,11 @@
## 思路
-这道题相对于[62.不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A) 就是有了障碍。
+这道题相对于[62.不同路径](https://programmercarl.com/0062.不同路径.html) 就是有了障碍。
第一次接触这种题目的同学可能会有点懵,这有障碍了,应该怎么算呢?
-[62.不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)中我们已经详细分析了没有障碍的情况,有障碍的话,其实就是标记对应的dp table(dp数组)保持初始值(0)就可以了。
+[62.不同路径](https://programmercarl.com/0062.不同路径.html)中我们已经详细分析了没有障碍的情况,有障碍的话,其实就是标记对应的dp table(dp数组)保持初始值(0)就可以了。
动规五部曲:
@@ -77,7 +77,7 @@ if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i
3. dp数组如何初始化
-在[62.不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)不同路径中我们给出如下的初始化:
+在[62.不同路径](https://programmercarl.com/0062.不同路径.html)不同路径中我们给出如下的初始化:
```
vector> dp(m, vector(n, 0)); // 初始值为0
@@ -97,7 +97,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1;
所以本题初始化代码为:
-```C++
+```CPP
vector> dp(m, vector(n, 0));
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
@@ -111,7 +111,7 @@ for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
代码如下:
-```C++
+```CPP
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (obstacleGrid[i][j] == 1) continue;
@@ -135,7 +135,7 @@ for (int i = 1; i < m; i++) {
动规五部分分析完毕,对应C++代码如下:
-```C++
+```CPP
class Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid) {
@@ -159,7 +159,7 @@ public:
## 总结
-本题是[62.不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)的障碍版,整体思路大体一致。
+本题是[62.不同路径](https://programmercarl.com/0062.不同路径.html)的障碍版,整体思路大体一致。
但就算是做过62.不同路径,在做本题也会有感觉遇到障碍无从下手。
@@ -341,4 +341,4 @@ var uniquePathsWithObstacles = function(obstacleGrid) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md
index 96899d37..4f049e4a 100644
--- a/problems/0070.爬楼梯.md
+++ b/problems/0070.爬楼梯.md
@@ -7,7 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
## 70. 爬楼梯
-题目地址:https://leetcode-cn.com/problems/climbing-stairs/
+[力扣题目链接](https://leetcode-cn.com/problems/climbing-stairs/)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
@@ -109,7 +109,7 @@ dp[i]: 爬到第i层楼梯,有dp[i]种方法
以上五部分析完之后,C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -130,7 +130,7 @@ public:
当然依然也可以,优化一下空间复杂度,代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -163,7 +163,7 @@ public:
这里我先给出我的实现代码:
-```C++
+```CPP
class Solution {
public:
int climbStairs(int n) {
@@ -196,9 +196,9 @@ public:
## 总结
-这道题目和[动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)题目基本是一样的,但是会发现本题相比[动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)难多了,为什么呢?
+这道题目和[动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html)题目基本是一样的,但是会发现本题相比[动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html)难多了,为什么呢?
-关键是 [动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w) 题目描述就已经把动规五部曲里的递归公式和如何初始化都给出来了,剩下几部曲也自然而然的推出来了。
+关键是 [动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html) 题目描述就已经把动规五部曲里的递归公式和如何初始化都给出来了,剩下几部曲也自然而然的推出来了。
而本题,就需要逐个分析了,大家现在应该初步感受出[关于动态规划,你该了解这些!](https://leetcode-cn.com/circle/article/tNuNnM/)里给出的动规五部曲了。
@@ -301,4 +301,4 @@ var climbStairs = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0070.爬楼梯完全背包版本.md b/problems/0070.爬楼梯完全背包版本.md
index 5c8270b6..4410dbaf 100644
--- a/problems/0070.爬楼梯完全背包版本.md
+++ b/problems/0070.爬楼梯完全背包版本.md
@@ -13,7 +13,7 @@
## 70. 爬楼梯
-链接:https://leetcode-cn.com/problems/climbing-stairs/
+[力扣题目链接](https://leetcode-cn.com/problems/climbing-stairs/)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
@@ -38,7 +38,7 @@
## 思路
-这道题目 我们在[动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw) 中已经讲过一次了,原题其实是一道简单动规的题目。
+这道题目 我们在[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html) 中已经讲过一次了,原题其实是一道简单动规的题目。
既然这么简单为什么还要讲呢,其实本题稍加改动就是一道面试好题。
@@ -52,7 +52,7 @@
**此时大家应该发现这就是一个完全背包问题了!**
-和昨天的题目[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)基本就是一道题了。
+和昨天的题目[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)基本就是一道题了。
动规五部曲分析如下:
@@ -62,7 +62,7 @@
2. 确定递推公式
-在[动态规划:494.目标和](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw) 、 [动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)、[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)中我们都讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];
+在[动态规划:494.目标和](https://programmercarl.com/0494.目标和.html) 、 [动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)、[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)中我们都讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];
本题呢,dp[i]有几种来源,dp[i - 1],dp[i - 2],dp[i - 3] 等等,即:dp[i - j]
@@ -84,7 +84,7 @@
5. 举例来推导dp数组
-介于本题和[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)几乎是一样的,这里我就不再重复举例了。
+介于本题和[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)几乎是一样的,这里我就不再重复举例了。
以上分析完毕,C++代码如下:
@@ -192,4 +192,4 @@ func climbStairs(n int) int {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0072.编辑距离.md b/problems/0072.编辑距离.md
index 9ddca7c0..2045c1fd 100644
--- a/problems/0072.编辑距离.md
+++ b/problems/0072.编辑距离.md
@@ -8,7 +8,7 @@
## 72. 编辑距离
-https://leetcode-cn.com/problems/edit-distance/
+[力扣题目链接](https://leetcode-cn.com/problems/edit-distance/)
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
@@ -35,7 +35,7 @@ inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
-
+
提示:
@@ -91,18 +91,18 @@ if (word1[i - 1] != word2[j - 1])
`if (word1[i - 1] != word2[j - 1])`,此时就需要编辑了,如何编辑呢?
-操作一:word1增加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 加上一个增加元素的操作。
+* 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。
即 `dp[i][j] = dp[i - 1][j] + 1;`
-操作二:word2添加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。
+* 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。
即 `dp[i][j] = dp[i][j - 1] + 1;`
-这里有同学发现了,怎么都是添加元素,删除元素去哪了。
+这里有同学发现了,怎么都是删除元素,添加元素去哪了。
-**word2添加一个元素,相当于word1删除一个元素**,例如 `word1 = "ad" ,word2 = "a"`,`word1`删除元素`'d'`,`word2`添加一个元素`'d'`,变成`word1="a", word2="ad"`, 最终的操作数是一样! dp数组如下图所示意的:
+**word2添加一个元素,相当于word1删除一个元素**,例如 `word1 = "ad" ,word2 = "a"`,`word1`删除元素`'d'` 和 `word2`添加一个元素`'d'`,变成`word1="a", word2="ad"`, 最终的操作数是一样! dp数组如下图所示意的:
```
a a d
@@ -123,7 +123,7 @@ if (word1[i - 1] != word2[j - 1])
递归公式代码如下:
-```C++
+```CPP
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
@@ -151,7 +151,7 @@ dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最
所以C++代码如下:
-```C++
+```CPP
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
```
@@ -175,7 +175,7 @@ for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
代码如下:
-```C++
+```CPP
for (int i = 1; i <= word1.size(); i++) {
for (int j = 1; j <= word2.size(); j++) {
if (word1[i - 1] == word2[j - 1]) {
@@ -198,7 +198,7 @@ for (int i = 1; i <= word1.size(); i++) {
以上动规五部分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int minDistance(string word1, string word2) {
@@ -338,4 +338,4 @@ const minDistance = (word1, word2) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0077.组合.md b/problems/0077.组合.md
index 0b289a40..9b44b572 100644
--- a/problems/0077.组合.md
+++ b/problems/0077.组合.md
@@ -11,7 +11,7 @@
# 第77题. 组合
-题目链接:https://leetcode-cn.com/problems/combinations/
+[力扣题目链接](https://leetcode-cn.com/problems/combinations/ )
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
@@ -80,7 +80,7 @@ for (int i = 1; i <= n; i++) {
如果脑洞模拟回溯搜索的过程,绝对可以让人窒息,所以需要抽象图形结构来进一步理解。
-**我们在[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中说道回溯法解决的问题都可以抽象为树形结构(N叉树),用树形结构来理解回溯就容易多了**。
+**我们在[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中说道回溯法解决的问题都可以抽象为树形结构(N叉树),用树形结构来理解回溯就容易多了**。
那么我把组合问题抽象为如下树形结构:
@@ -100,7 +100,7 @@ for (int i = 1; i <= n; i++) {
相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。
-在[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中我们提到了回溯法三部曲,那么我们按照回溯法三部曲开始正式讲解代码了。
+在[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中我们提到了回溯法三部曲,那么我们按照回溯法三部曲开始正式讲解代码了。
## 回溯法三部曲
@@ -173,7 +173,7 @@ for循环每次从startIndex开始遍历,然后用path保存取到的节点i
代码如下:
-```C++
+```CPP
for (int i = startIndex; i <= n; i++) { // 控制树的横向遍历
path.push_back(i); // 处理节点
backtracking(n, k, i + 1); // 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始
@@ -188,7 +188,7 @@ backtracking的下面部分就是回溯的操作了,撤销本次处理的结
关键地方都讲完了,组合问题C++完整代码如下:
-```C++
+```CPP
class Solution {
private:
vector> result; // 存放符合条件结果的集合
@@ -214,7 +214,7 @@ public:
};
```
-还记得我们在[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中给出的回溯法模板么?
+还记得我们在[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中给出的回溯法模板么?
如下:
```
@@ -435,9 +435,62 @@ func backtrack(n,k,start int,track []int){
}
```
+C:
+```c
+int* path;
+int pathTop;
+int** ans;
+int ansTop;
+
+void backtracking(int n, int k,int startIndex) {
+ //当path中元素个数为k个时,我们需要将path数组放入ans二维数组中
+ if(pathTop == k) {
+ //path数组为我们动态申请,若直接将其地址放入二维数组,path数组中的值会随着我们回溯而逐渐变化
+ //因此创建新的数组存储path中的值
+ int* temp = (int*)malloc(sizeof(int) * k);
+ int i;
+ for(i = 0; i < k; i++) {
+ temp[i] = path[i];
+ }
+ ans[ansTop++] = temp;
+ return ;
+ }
+
+ int j;
+ for(j = startIndex; j <=n ;j++) {
+ //将当前结点放入path数组
+ path[pathTop++] = j;
+ //进行递归
+ backtracking(n, k, j + 1);
+ //进行回溯,将数组最上层结点弹出
+ pathTop--;
+ }
+}
+
+int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
+ //path数组存储符合条件的结果
+ path = (int*)malloc(sizeof(int) * k);
+ //ans二维数组存储符合条件的结果数组的集合。(数组足够大,避免极端情况)
+ ans = (int**)malloc(sizeof(int*) * 10000);
+ pathTop = ansTop = 0;
+
+ //回溯算法
+ backtracking(n, k, 1);
+ //最后的返回大小为ans数组大小
+ *returnSize = ansTop;
+ //returnColumnSizes数组存储ans二维数组对应下标中一维数组的长度(都为k)
+ *returnColumnSizes = (int*)malloc(sizeof(int) *(*returnSize));
+ int i;
+ for(i = 0; i < *returnSize; i++) {
+ (*returnColumnSizes)[i] = k;
+ }
+ //返回ans二维数组
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0077.组合优化.md b/problems/0077.组合优化.md
index d3e82f09..136ceb34 100644
--- a/problems/0077.组合优化.md
+++ b/problems/0077.组合优化.md
@@ -10,7 +10,7 @@
-在[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)中,我们通过回溯搜索法,解决了n个数中求k个数的组合问题。
+在[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)中,我们通过回溯搜索法,解决了n个数中求k个数的组合问题。
> 可以直接看我的B栈视频讲解:[带你学透回溯算法-组合问题的剪枝操作](https://www.bilibili.com/video/BV1wi4y157er)
@@ -18,7 +18,7 @@
链接:https://leetcode-cn.com/problems/combinations/
-**看本篇之前,需要先看[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)**。
+**看本篇之前,需要先看[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)**。
大家先回忆一下[77. 组合]给出的回溯法的代码:
@@ -242,11 +242,62 @@ var combine = function(n, k) {
};
```
+C:
+```c
+int* path;
+int pathTop;
+int** ans;
+int ansTop;
+void backtracking(int n, int k,int startIndex) {
+ //当path中元素个数为k个时,我们需要将path数组放入ans二维数组中
+ if(pathTop == k) {
+ //path数组为我们动态申请,若直接将其地址放入二维数组,path数组中的值会随着我们回溯而逐渐变化
+ //因此创建新的数组存储path中的值
+ int* temp = (int*)malloc(sizeof(int) * k);
+ int i;
+ for(i = 0; i < k; i++) {
+ temp[i] = path[i];
+ }
+ ans[ansTop++] = temp;
+ return ;
+ }
+ int j;
+ for(j = startIndex; j <= n- (k - pathTop) + 1;j++) {
+ //将当前结点放入path数组
+ path[pathTop++] = j;
+ //进行递归
+ backtracking(n, k, j + 1);
+ //进行回溯,将数组最上层结点弹出
+ pathTop--;
+ }
+}
+
+int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
+ //path数组存储符合条件的结果
+ path = (int*)malloc(sizeof(int) * k);
+ //ans二维数组存储符合条件的结果数组的集合。(数组足够大,避免极端情况)
+ ans = (int**)malloc(sizeof(int*) * 10000);
+ pathTop = ansTop = 0;
+
+ //回溯算法
+ backtracking(n, k, 1);
+ //最后的返回大小为ans数组大小
+ *returnSize = ansTop;
+ //returnColumnSizes数组存储ans二维数组对应下标中一维数组的长度(都为k)
+ *returnColumnSizes = (int*)malloc(sizeof(int) *(*returnSize));
+ int i;
+ for(i = 0; i < *returnSize; i++) {
+ (*returnColumnSizes)[i] = k;
+ }
+ //返回ans二维数组
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0078.子集.md b/problems/0078.子集.md
index 0b2f3c09..583fe664 100644
--- a/problems/0078.子集.md
+++ b/problems/0078.子集.md
@@ -9,7 +9,7 @@
## 第78题. 子集
-题目地址:https://leetcode-cn.com/problems/subsets/
+[力扣题目链接](https://leetcode-cn.com/problems/subsets/)
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
@@ -31,7 +31,7 @@
## 思路
-求子集问题和[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)和[回溯算法:分割问题!](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)又不一样了。
+求子集问题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:分割问题!](https://programmercarl.com/0131.分割回文串.html)又不一样了。
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,**那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!**
@@ -101,7 +101,7 @@ for (int i = startIndex; i < nums.size(); i++) {
## C++代码
-根据[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)给出的回溯算法模板:
+根据[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)给出的回溯算法模板:
```
void backtracking(参数) {
@@ -120,7 +120,7 @@ void backtracking(参数) {
可以写出如下回溯算法C++代码:
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -157,15 +157,15 @@ public:
相信大家经过了
* 组合问题:
- * [回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)
- * [回溯算法:组合问题再剪剪枝](https://mp.weixin.qq.com/s/Ri7spcJMUmph4c6XjPWXQA)
- * [回溯算法:求组合总和!](https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3w)
- * [回溯算法:电话号码的字母组合](https://mp.weixin.qq.com/s/e2ua2cmkE_vpYjM3j6HY0A)
- * [回溯算法:求组合总和(二)](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)
- * [回溯算法:求组合总和(三)](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)
+ * [回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)
+ * [回溯算法:组合问题再剪剪枝](https://programmercarl.com/0077.组合优化.html)
+ * [回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)
+ * [回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
+ * [回溯算法:求组合总和(二)](https://programmercarl.com/0039.组合总和.html)
+ * [回溯算法:求组合总和(三)](https://programmercarl.com/0040.组合总和II.html)
* 分割问题:
- * [回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)
- * [回溯算法:复原IP地址](https://mp.weixin.qq.com/s/v--VmA8tp9vs4bXCqHhBuA)
+ * [回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)
+ * [回溯算法:复原IP地址](https://programmercarl.com/0093.复原IP地址.html)
洗礼之后,发现子集问题还真的有点简单了,其实这就是一道标准的模板题。
@@ -268,4 +268,4 @@ var subsets = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0084.柱状图中最大的矩形.md b/problems/0084.柱状图中最大的矩形.md
new file mode 100644
index 00000000..57df4161
--- /dev/null
+++ b/problems/0084.柱状图中最大的矩形.md
@@ -0,0 +1,324 @@
+
+
+# 84.柱状图中最大的矩形
+
+[力扣题目链接](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
+
+给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
+
+求在该柱状图中,能够勾勒出来的矩形的最大面积。
+
+
+
+
+
+
+# 思路
+
+本题和[42. 接雨水](https://programmercarl.com/0042.接雨水.html),是遥相呼应的两道题目,建议都要仔细做一做,原理上有很多相同的地方,但细节上又有差异,更可以加深对单调栈的理解!
+
+其实这两道题目先做那一道都可以,但我先写的42.接雨水的题解,所以如果没做过接雨水的话,建议先做一做接雨水,可以参考我的题解:[42. 接雨水](https://programmercarl.com/0042.接雨水.html)
+
+我们先来看一下双指针的解法:
+
+## 双指针解法
+
+```CPP
+class Solution {
+public:
+ int largestRectangleArea(vector& heights) {
+ int sum = 0;
+ for (int i = 0; i < heights.size(); i++) {
+ int left = i;
+ int right = i;
+ for (; left >= 0; left--) {
+ if (heights[left] < heights[i]) break;
+ }
+ for (; right < heights.size(); right++) {
+ if (heights[right] < heights[i]) break;
+ }
+ int w = right - left - 1;
+ int h = heights[i];
+ sum = max(sum, w * h);
+ }
+ return sum;
+ }
+};
+```
+
+如上代码并不能通过leetcode,超时了,因为时间复杂度是O(n^2)。
+
+## 动态规划
+
+本题动态规划的写法整体思路和[42. 接雨水](https://programmercarl.com/0042.接雨水.html)是一致的,但要比[42. 接雨水](https://programmercarl.com/0042.接雨水.html)难一些。
+
+难就难在本题要记录记录每个柱子 左边第一个小于该柱子的下标,而不是左边第一个小于该柱子的高度。
+
+所以需要循环查找,也就是下面在寻找的过程中使用了while,详细请看下面注释,整理思路在题解:[42. 接雨水](https://programmercarl.com/0042.接雨水.html)中已经介绍了。
+
+```CPP
+class Solution {
+public:
+ int largestRectangleArea(vector& heights) {
+ vector minLeftIndex(heights.size());
+ vector minRightIndex(heights.size());
+ int size = heights.size();
+
+ // 记录每个柱子 左边第一个小于该柱子的下标
+ minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
+ for (int i = 1; i < size; i++) {
+ int t = i - 1;
+ // 这里不是用if,而是不断向左寻找的过程
+ while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
+ minLeftIndex[i] = t;
+ }
+ // 记录每个柱子 右边第一个小于该柱子的下标
+ minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
+ for (int i = size - 2; i >= 0; i--) {
+ int t = i + 1;
+ // 这里不是用if,而是不断向右寻找的过程
+ while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
+ minRightIndex[i] = t;
+ }
+ // 求和
+ int result = 0;
+ for (int i = 0; i < size; i++) {
+ int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
+ result = max(sum, result);
+ }
+ return result;
+ }
+};
+```
+
+## 单调栈
+
+本地单调栈的解法和接雨水的题目是遥相呼应的。
+
+为什么这么说呢,[42. 接雨水](https://programmercarl.com/0042.接雨水.html)是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。
+
+**这里就涉及到了单调栈很重要的性质,就是单调栈里的顺序,是从小到大还是从大到小**。
+
+在题解[42. 接雨水](https://programmercarl.com/0042.接雨水.html)中我讲解了接雨水的单调栈从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
+
+那么因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!
+
+我来举一个例子,如图:
+
+
+
+只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
+
+所以本题单调栈的顺序正好与接雨水反过来。
+
+此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度**
+
+理解这一点,对单调栈就掌握的比较到位了。
+
+除了栈内元素顺序和接雨水不同,剩下的逻辑就都差不多了,在题解[42. 接雨水](https://programmercarl.com/0042.接雨水.html)我已经对单调栈的各个方面做了详细讲解,这里就不赘述了。
+
+剩下就是分析清楚如下三种情况:
+
+* 情况一:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况
+* 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
+* 情况三:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况
+
+C++代码如下:
+
+```CPP
+// 版本一
+class Solution {
+public:
+ int largestRectangleArea(vector& heights) {
+ stack st;
+ heights.insert(heights.begin(), 0); // 数组头部加入元素0
+ heights.push_back(0); // 数组尾部加入元素0
+ st.push(0);
+ int result = 0;
+ // 第一个元素已经入栈,从下表1开始
+ for (int i = 1; i < heights.size(); i++) {
+ // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下表
+ if (heights[i] > heights[st.top()]) {
+ st.push(i);
+ } else if (heights[i] == heights[st.top()]) {
+ st.pop(); // 这个可以加,可以不加,效果一样,思路不同
+ st.push(i);
+ } else {
+ while (heights[i] < heights[st.top()]) { // 注意是while
+ int mid = st.top();
+ st.pop();
+ int left = st.top();
+ int right = i;
+ int w = right - left - 1;
+ int h = heights[mid];
+ result = max(result, w * h);
+ }
+ st.push(i);
+ }
+ }
+ return result;
+ }
+};
+
+```
+
+代码精简之后:
+
+```CPP
+// 版本二
+class Solution {
+public:
+ int largestRectangleArea(vector& heights) {
+ stack st;
+ heights.insert(heights.begin(), 0); // 数组头部加入元素0
+ heights.push_back(0); // 数组尾部加入元素0
+ st.push(0);
+ int result = 0;
+ for (int i = 1; i < heights.size(); i++) {
+ while (heights[i] < heights[st.top()]) {
+ int mid = st.top();
+ st.pop();
+ int w = i - st.top() - 1;
+ int h = heights[mid];
+ result = max(result, w * h);
+ }
+ st.push(i);
+ }
+ return result;
+ }
+};
+```
+
+这里我依然建议大家按部就班把版本一写出来,把情况一二三分析清楚,然后在精简代码到版本二。 直接看版本二容易忽略细节!
+
+## 其他语言版本
+
+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;
+ }
+}
+```
+
+单调栈
+```java
+class Solution {
+ int largestRectangleArea(int[] heights) {
+ Stack st = new Stack();
+
+ // 数组扩容,在头和尾各加入一个元素
+ int [] newHeights = new int[heights.length + 2];
+ newHeights[0] = 0;
+ newHeights[newHeights.length - 1] = 0;
+ for (int index = 0; index < heights.length; index++){
+ newHeights[index + 1] = heights[index];
+ }
+
+ heights = newHeights;
+
+ st.push(0);
+ int result = 0;
+ // 第一个元素已经入栈,从下表1开始
+ for (int i = 1; i < heights.length; i++) {
+ // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下表
+ if (heights[i] > heights[st.peek()]) {
+ st.push(i);
+ } else if (heights[i] == heights[st.peek()]) {
+ st.pop(); // 这个可以加,可以不加,效果一样,思路不同
+ st.push(i);
+ } else {
+ while (heights[i] < heights[st.peek()]) { // 注意是while
+ int mid = st.peek();
+ st.pop();
+ int left = st.peek();
+ int right = i;
+ int w = right - left - 1;
+ int h = heights[mid];
+ result = Math.max(result, w * h);
+ }
+ st.push(i);
+ }
+ }
+ return result;
+ }
+}
+```
+
+Python:
+
+动态规划
+```python3
+class Solution:
+ def largestRectangleArea(self, heights: List[int]) -> int:
+ result = 0
+ minleftindex, minrightindex = [0]*len(heights), [0]*len(heights)
+
+ minleftindex[0]=-1
+ for i in range(1,len(heights)):
+ t = i-1
+ while t>=0 and heights[t]>=heights[i]: t=minleftindex[t]
+ minleftindex[i]=t
+
+ minrightindex[-1]=len(heights)
+ for i in range(len(heights)-2,-1,-1):
+ t=i+1
+ while t=heights[i]: t=minrightindex[t]
+ minrightindex[i]=t
+
+ for i in range(0,len(heights)):
+ left = minleftindex[i]
+ right = minrightindex[i]
+ summ = (right-left-1)*heights[i]
+ result = max(result,summ)
+ return result
+```
+单调栈 版本二
+```python3
+class Solution:
+ def largestRectangleArea(self, heights: List[int]) -> int:
+ heights.insert(0,0) # 数组头部加入元素0
+ heights.append(0) # 数组尾部加入元素0
+ st = [0]
+ result = 0
+ for i in range(1,len(heights)):
+ while st!=[] and heights[i]
diff --git a/problems/0090.子集II.md b/problems/0090.子集II.md
index 71aef5c7..6dc631de 100644
--- a/problems/0090.子集II.md
+++ b/problems/0090.子集II.md
@@ -9,7 +9,7 @@
## 第90题.子集II
-题目链接:https://leetcode-cn.com/problems/subsets-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/subsets-ii/)
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
@@ -30,11 +30,11 @@
## 思路
-做本题之前一定要先做[78.子集](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)。
+做本题之前一定要先做[78.子集](https://programmercarl.com/0078.子集.html)。
-这道题目和[回溯算法:求子集问题!](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)区别就是集合里有重复元素了,而且求取的子集要去重。
+这道题目和[回溯算法:求子集问题!](https://programmercarl.com/0078.子集.html)区别就是集合里有重复元素了,而且求取的子集要去重。
-那么关于回溯算法中的去重问题,**在[40.组合总和II](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)中已经详细讲解过了,和本题是一个套路**。
+那么关于回溯算法中的去重问题,**在[40.组合总和II](https://programmercarl.com/0040.组合总和II.html)中已经详细讲解过了,和本题是一个套路**。
**剧透一下,后期要讲解的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要**。
@@ -44,11 +44,11 @@
从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
-本题就是其实就是[回溯算法:求子集问题!](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)的基础上加上了去重,去重我们在[回溯算法:求组合总和(三)](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)也讲过了,所以我就直接给出代码了:
+本题就是其实就是[回溯算法:求子集问题!](https://programmercarl.com/0078.子集.html)的基础上加上了去重,去重我们在[回溯算法:求组合总和(三)](https://programmercarl.com/0040.组合总和II.html)也讲过了,所以我就直接给出代码了:
## C++代码
-```
+```c++
class Solution {
private:
vector> result;
@@ -80,11 +80,10 @@ public:
return result;
}
};
-
```
使用set去重的版本。
-```
+```c++
class Solution {
private:
vector> result;
@@ -113,7 +112,6 @@ public:
return result;
}
};
-
```
## 补充
@@ -124,7 +122,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -151,7 +149,6 @@ public:
return result;
}
};
-
```
## 总结
@@ -288,4 +285,4 @@ var subsetsWithDup = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md
index 40ad7684..9f2ea6e7 100644
--- a/problems/0093.复原IP地址.md
+++ b/problems/0093.复原IP地址.md
@@ -10,7 +10,7 @@
## 93.复原IP地址
-题目地址:https://leetcode-cn.com/problems/restore-ip-addresses/
+[力扣题目链接](https://leetcode-cn.com/problems/restore-ip-addresses/)
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
@@ -45,11 +45,11 @@ s 仅由数字组成
## 思路
-做这道题目之前,最好先把[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)这个做了。
+做这道题目之前,最好先把[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)这个做了。
这道题目相信大家刚看的时候,应该会一脸茫然。
-其实只要意识到这是切割问题,**切割问题就可以使用回溯搜索法把所有可能性搜出来**,和刚做过的[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)就十分类似了。
+其实只要意识到这是切割问题,**切割问题就可以使用回溯搜索法把所有可能性搜出来**,和刚做过的[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)就十分类似了。
切割问题可以抽象为树型结构,如图:
@@ -60,7 +60,7 @@ s 仅由数字组成
* 递归参数
-在[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)中我们就提到切割问题类似组合问题。
+在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中我们就提到切割问题类似组合问题。
startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。
@@ -76,7 +76,7 @@ startIndex一定是需要的,因为不能重复分割,记录下一层递归
* 递归终止条件
-终止条件和[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)情况就不同了,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。
+终止条件和[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)情况就不同了,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。
pointNum表示逗点数量,pointNum为3说明字符串分成了4段了。
@@ -96,7 +96,7 @@ if (pointNum == 3) { // 逗点数量为3时,分隔结束
* 单层搜索的逻辑
-在[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)中已经讲过在循环遍历中如何截取子串。
+在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中已经讲过在循环遍历中如何截取子串。
在`for (int i = startIndex; i < s.size(); i++)`循环中 [startIndex, i]这个区间就是截取的子串,需要判断这个子串是否合法。
@@ -164,7 +164,7 @@ bool isValid(const string& s, int start, int end) {
## C++代码
-根据[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)给出的回溯算法模板:
+根据[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)给出的回溯算法模板:
```
void backtracking(参数) {
@@ -183,7 +183,7 @@ void backtracking(参数) {
可以写出如下回溯算法C++代码:
-```C++
+```CPP
class Solution {
private:
vector result;// 记录结果
@@ -239,11 +239,11 @@ public:
## 总结
-在[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)中我列举的分割字符串的难点,本题都覆盖了。
+在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中我列举的分割字符串的难点,本题都覆盖了。
而且本题还需要操作字符串添加逗号作为分隔符,并验证区间的合法性。
-可以说是[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)的加强版。
+可以说是[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)的加强版。
在本文的树形结构图中,我已经把详细的分析思路都画了出来,相信大家看了之后一定会思路清晰不少!
@@ -309,7 +309,35 @@ class Solution {
```
python版本:
+```python
+class Solution:
+ def restoreIpAddresses(self, s: str) -> List[str]:
+ res = []
+ path = [] # 存放分割后的字符
+ # 判断数组中的数字是否合法
+ def isValid(p):
+ if p == '0': return True # 解决"0000"
+ if p[0] == '0': return False
+ if int(p) > 0 and int(p) <256: return True
+ return False
+ def backtrack(s, startIndex):
+ if len(s) > 12: return # 字符串长度最大为12
+ if len(path) == 4 and startIndex == len(s): # 确保切割完,且切割后的长度为4
+ res.append(".".join(path[:])) # 字符拼接
+ return
+
+ for i in range(startIndex, len(s)):
+ if len(s) - startIndex > 3*(4 - len(path)): continue # 剪枝,剩下的字符串大于允许的最大长度则跳过
+ p = s[startIndex:i+1] # 分割字符
+ if isValid(p): # 判断字符是否有效
+ path.append(p)
+ else: continue
+ backtrack(s, i + 1) # 寻找i+1为起始位置的子串
+ path.pop()
+ backtrack(s, 0)
+ return res
+```
```python
class Solution(object):
def restoreIpAddresses(self, s):
@@ -453,4 +481,4 @@ func isNormalIp(s string,startIndex,end int)bool{
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0096.不同的二叉搜索树.md b/problems/0096.不同的二叉搜索树.md
index 56f50d46..68ab11c8 100644
--- a/problems/0096.不同的二叉搜索树.md
+++ b/problems/0096.不同的二叉搜索树.md
@@ -8,7 +8,7 @@
## 96.不同的二叉搜索树
-题目链接:https://leetcode-cn.com/problems/unique-binary-search-trees/
+[力扣题目链接](https://leetcode-cn.com/problems/unique-binary-search-trees/)
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
@@ -20,7 +20,7 @@
这道题目描述很简短,但估计大部分同学看完都是懵懵的状态,这得怎么统计呢?
-关于什么是二叉搜索树,我们之前在讲解二叉树专题的时候已经详细讲解过了,也可以看看这篇[二叉树:二叉搜索树登场!](https://mp.weixin.qq.com/s/vsKrWRlETxCVsiRr8v_hHg)在回顾一波。
+关于什么是二叉搜索树,我们之前在讲解二叉树专题的时候已经详细讲解过了,也可以看看这篇[二叉树:二叉搜索树登场!](https://programmercarl.com/0700.二叉搜索树中的搜索.html)在回顾一波。
了解了二叉搜索树之后,我们应该先举几个例子,画画图,看看有没有什么规律,如图:
@@ -103,7 +103,7 @@ j相当于是头结点的元素,从1遍历到i为止。
代码如下:
-```C++
+```CPP
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
@@ -123,7 +123,7 @@ n为5时候的dp数组状态如图:
综上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int numTrees(int n) {
@@ -234,4 +234,4 @@ const numTrees =(n) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md
index 248d10f1..6d054634 100644
--- a/problems/0098.验证二叉搜索树.md
+++ b/problems/0098.验证二叉搜索树.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 98.验证二叉搜索树
+# 98.验证二叉搜索树
-题目地址:https://leetcode-cn.com/problems/validate-binary-search-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/validate-binary-search-tree/)
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
@@ -22,7 +22,7 @@

-## 思路
+# 思路
要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。
@@ -32,7 +32,7 @@
可以递归中序遍历将二叉搜索树转变成一个数组,代码如下:
-```C++
+```CPP
vector vec;
void traversal(TreeNode* root) {
if (root == NULL) return;
@@ -44,7 +44,7 @@ void traversal(TreeNode* root) {
然后只要比较一下,这个数组是否是有序的,**注意二叉搜索树中不能有重复元素**。
-```C++
+```CPP
traversal(root);
for (int i = 1; i < vec.size(); i++) {
// 注意要小于等于,搜索树里不能有相同元素
@@ -55,7 +55,7 @@ return true;
整体代码如下:
-```C++
+```CPP
class Solution {
private:
vector vec;
@@ -103,7 +103,7 @@ if (root->val > root->left->val && root->val < root->right->val) {

-节点10小于左节点5,大于右节点15,但右子树里出现了一个6 这就不符合了!
+节点10大于左节点5,小于右节点15,但右子树里出现了一个6 这就不符合了!
* 陷阱2
@@ -121,7 +121,7 @@ if (root->val > root->left->val && root->val < root->right->val) {
要定义一个longlong的全局变量,用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为longlong的类型,初始化为longlong最小值。
-注意递归函数要有bool类型的返回值, 我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg) 中讲了,只有寻找某一条边(或者一个节点)的时候,递归函数会有bool类型的返回值。
+注意递归函数要有bool类型的返回值, 我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://programmercarl.com/0112.路径总和.html) 中讲了,只有寻找某一条边(或者一个节点)的时候,递归函数会有bool类型的返回值。
其实本题是同样的道理,我们在寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整个树,如果找到不符合的节点了,立刻返回。
@@ -163,7 +163,7 @@ return left && right;
整体代码如下:
-```C++
+```CPP
class Solution {
public:
long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
@@ -189,7 +189,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* pre = NULL; // 用来记录前一个节点
@@ -210,11 +210,11 @@ public:
## 迭代法
-可以用迭代法模拟二叉树中序遍历,对前中后序迭代法生疏的同学可以看这两篇[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg),[二叉树:前中后序迭代方式统一写法](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)
+可以用迭代法模拟二叉树中序遍历,对前中后序迭代法生疏的同学可以看这两篇[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html),[二叉树:前中后序迭代方式统一写法](https://programmercarl.com/二叉树的统一迭代法.html)
迭代法中序遍历稍加改动就可以了,代码如下:
-```C++
+```CPP
class Solution {
public:
bool isValidBST(TreeNode* root) {
@@ -240,9 +240,9 @@ public:
};
```
-在[二叉树:二叉搜索树登场!](https://mp.weixin.qq.com/s/vsKrWRlETxCVsiRr8v_hHg)中我们分明写出了痛哭流涕的简洁迭代法,怎么在这里不行了呢,因为本题是要验证二叉搜索树啊。
+在[二叉树:二叉搜索树登场!](https://programmercarl.com/0700.二叉搜索树中的搜索.html)中我们分明写出了痛哭流涕的简洁迭代法,怎么在这里不行了呢,因为本题是要验证二叉搜索树啊。
-## 总结
+# 总结
这道题目是一个简单题,但对于没接触过的同学还是有难度的。
@@ -251,10 +251,10 @@ public:
只要把基本类型的题目都做过,总结过之后,思路自然就开阔了。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -336,38 +336,62 @@ class Solution {
}
```
-Python:
+## Python
+
+**递归** - 利用BST中序遍历特性,把树"压缩"成数组
```python
-# 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 isValidBST(self, root: TreeNode) -> bool:
- res = [] //把二叉搜索树按中序遍历写成list
- def buildalist(root):
- if not root: return
- buildalist(root.left) //左
- res.append(root.val) //中
- buildalist(root.right) //右
- return res
- buildalist(root)
- return res == sorted(res) and len(set(res)) == len(res) //检查list里的数有没有重复元素,以及是否按从小到大排列
+ # 思路: 利用BST中序遍历的特性.
+ # 中序遍历输出的二叉搜索树节点的数值是有序序列
+ candidate_list = []
+
+ def __traverse(root: TreeNode) -> None:
+ nonlocal candidate_list
+ if not root:
+ return
+ __traverse(root.left)
+ candidate_list.append(root.val)
+ __traverse(root.right)
+
+ def __is_sorted(nums: list) -> bool:
+ for i in range(1, len(nums)):
+ if nums[i] <= nums[i - 1]: # ⚠️ 注意: Leetcode定义二叉搜索树中不能有重复元素
+ return False
+ return True
+
+ __traverse(root)
+ res = __is_sorted(candidate_list)
+
+ return res
+```
-# 简单递归法
+**递归** - 标准做法
+
+```python
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
- def isBST(root, min_val, max_val):
- if not root: return True
- if root.val >= max_val or root.val <= min_val:
+ # 规律: BST的中序遍历节点数值是从小到大.
+ cur_max = -float("INF")
+ def __isValidBST(root: TreeNode) -> bool:
+ nonlocal cur_max
+
+ if not root:
+ return True
+
+ is_left_valid = __isValidBST(root.left)
+ if cur_max < root.val:
+ cur_max = root.val
+ else:
return False
- return isBST(root.left, min_val, root.val) and isBST(root.right, root.val, max_val)
- return isBST(root, float("-inf"), float("inf"))
+ is_right_valid = __isValidBST(root.right)
+
+ return is_left_valid and is_right_valid
+ return __isValidBST(root)
+```
-# 迭代-中序遍历
+```python
+迭代-中序遍历
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
stack = []
@@ -386,7 +410,8 @@ class Solution:
return True
```
-Go:
+## Go
+
```Go
import "math"
@@ -429,9 +454,9 @@ func isValidBST(root *TreeNode) bool {
}
```
-JavaScript版本
+## JavaScript
-> 辅助数组解决
+辅助数组解决
```javascript
/**
@@ -464,7 +489,7 @@ var isValidBST = function (root) {
};
```
-> 递归中解决
+递归中解决
```javascript
/**
@@ -504,4 +529,4 @@ var isValidBST = function (root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0100.相同的树.md b/problems/0100.相同的树.md
index b536ffe3..6ffaf3cb 100644
--- a/problems/0100.相同的树.md
+++ b/problems/0100.相同的树.md
@@ -10,7 +10,7 @@
# 100. 相同的树
-题目地址:https://leetcode-cn.com/problems/same-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/same-tree/)
给定两个二叉树,编写一个函数来检验它们是否相同。
@@ -23,11 +23,11 @@
# 思路
-在[101.对称二叉树](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)中,我们讲到对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了**其实我们要比较的是两个树(这两个树是根节点的左右子树)**,所以在递归遍历的过程中,也是要同时遍历两棵树。
+在[101.对称二叉树](https://programmercarl.com/0101.对称二叉树.html)中,我们讲到对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了**其实我们要比较的是两个树(这两个树是根节点的左右子树)**,所以在递归遍历的过程中,也是要同时遍历两棵树。
理解这一本质之后,就会发现,求二叉树是否对称,和求二叉树是否相同几乎是同一道题目。
-**如果没有读过[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)这一篇,请认真读完再做这道题,就会有感觉了。**
+**如果没有读过[二叉树:我对称么?](https://programmercarl.com/0101.对称二叉树.html)这一篇,请认真读完再做这道题,就会有感觉了。**
递归三部曲中:
@@ -42,7 +42,7 @@
bool compare(TreeNode* tree1, TreeNode* tree2)
```
-分析过程同[101.对称二叉树](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)。
+分析过程同[101.对称二叉树](https://programmercarl.com/0101.对称二叉树.html)。
2. 确定终止条件
@@ -61,14 +61,14 @@ bool compare(TreeNode* tree1, TreeNode* tree2)
此时tree1、tree2节点不为空,且数值也不相同的情况我们也处理了。
代码如下:
-```C++
+```CPP
if (tree1 == NULL && tree2 != NULL) return false;
else if (tree1 != NULL && tree2 == NULL) return false;
else if (tree1 == NULL && tree2 == NULL) return true;
else if (tree1->val != tree2->val) return false; // 注意这里我没有使用else
```
-分析过程同[101.对称二叉树](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)
+分析过程同[101.对称二叉树](https://programmercarl.com/0101.对称二叉树.html)
3. 确定单层递归的逻辑
@@ -77,7 +77,7 @@ else if (tree1->val != tree2->val) return false; // 注意这里我没有
代码如下:
-```C++
+```CPP
bool left = compare(tree1->left, tree2->left); // 左子树:左、 右子树:左
bool right = compare(tree1->right, tree2->right); // 左子树:右、 右子树:右
bool isSame = left && right; // 左子树:中、 右子树:中(逻辑处理)
@@ -85,7 +85,7 @@ return isSame;
```
最后递归的C++整体代码如下:
-```C++
+```CPP
class Solution {
public:
bool compare(TreeNode* tree1, TreeNode* tree2) {
@@ -119,7 +119,7 @@ public:
## 递归
-```C++
+```CPP
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
@@ -138,30 +138,30 @@ public:
## 迭代法
-```C++
-lass Solution {
+```CPP
+class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if (p == NULL && q == NULL) return true;
if (p == NULL || q == NULL) return false;
queue que;
- que.push(p); //
- que.push(q); //
+ que.push(p); // 添加根节点p
+ que.push(q); // 添加根节点q
while (!que.empty()) { //
TreeNode* leftNode = que.front(); que.pop();
TreeNode* rightNode = que.front(); que.pop();
- if (!leftNode && !rightNode) { //
+ if (!leftNode && !rightNode) { // 若p的节点与q的节点都为空
continue;
}
- //
+ // 若p的节点与q的节点有一个为空或p的节点的值与q节点不同
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
return false;
}
- que.push(leftNode->left); //
- que.push(rightNode->left); //
- que.push(leftNode->right); //
- que.push(rightNode->right); //
+ que.push(leftNode->left); // 添加p节点的左子树节点
+ que.push(rightNode->left); // 添加q节点的左子树节点
+ que.push(leftNode->right); // 添加p节点的右子树节点
+ que.push(rightNode->right); // 添加q节点的右子树节点
}
return true;
}
@@ -172,8 +172,72 @@ public:
Java:
-Python:
+```java
+// 递归法
+class Solution {
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ if (p == null && q == null) return true;
+ else if (q == null || p == null) return false;
+ else if (q.val != p.val) return false;
+ return isSameTree(q.left, p.left) && isSameTree(q.right, p.right);
+ }
+}
+```
+```java
+// 迭代法
+class Solution {
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ if(p == null && q == null) return true;
+ if(p == null || q == null) return false;
+ Queue que= new LinkedList();
+ que.offer(p);
+ que.offer(q);
+ while(!que.isEmpty()){
+ TreeNode leftNode = que.poll();
+ TreeNode rightNode = que.poll();
+ if(leftNode == null && rightNode == null) continue;
+ if(leftNode == null || rightNode== null || leftNode.val != rightNode.val) return false;
+ que.offer(leftNode.left);
+ que.offer(rightNode.left);
+ que.offer(leftNode.right);
+ que.offer(rightNode.right);
+ }
+ return true;
+ }
+}
+```
+Python:
+```python
+# 递归法
+class Solution:
+ def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
+ if not p and not q: return True
+ elif not p or not q: return False
+ elif p.val != q.val: return False
+ return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
+```
+
+```python
+# 迭代法
+class Solution:
+ def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
+ if not p and not q: return True
+ if not p or not q: return False
+ que = collections.deque()
+ que.append(p)
+ que.append(q)
+ while que:
+ leftNode = que.popleft()
+ rightNode = que.popleft()
+ if not leftNode and not rightNode: continue
+ if not leftNode or not rightNode or leftNode.val != rightNode.val: return False
+ que.append(leftNode.left)
+ que.append(rightNode.left)
+ que.append(leftNode.right)
+ que.append(rightNode.right)
+ return True
+```
Go:
JavaScript:
@@ -182,5 +246,5 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0101.对称二叉树.md b/problems/0101.对称二叉树.md
index 241564e9..03d3acaa 100644
--- a/problems/0101.对称二叉树.md
+++ b/problems/0101.对称二叉树.md
@@ -9,7 +9,7 @@
# 101. 对称二叉树
-题目地址:https://leetcode-cn.com/problems/symmetric-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/symmetric-tree/)
给定一个二叉树,检查它是否是镜像对称的。
@@ -73,7 +73,7 @@ bool compare(TreeNode* left, TreeNode* right)
此时左右节点不为空,且数值也不相同的情况我们也处理了。
代码如下:
-```C++
+```CPP
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
@@ -93,7 +93,7 @@ else if (left->val != right->val) return false; // 注意这里我没有
代码如下:
-```C++
+```CPP
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
@@ -104,7 +104,7 @@ return isSame;
最后递归的C++整体代码如下:
-```C++
+```CPP
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
@@ -137,7 +137,7 @@ public:
**盲目的照着抄,结果就是:发现这是一道“简单题”,稀里糊涂的就过了,但是真正的每一步判断逻辑未必想到清楚。**
当然我可以把如上代码整理如下:
-```C++
+```CPP
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
@@ -177,7 +177,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
bool isSymmetric(TreeNode* root) {
@@ -212,7 +212,7 @@ public:
只要把队列原封不动的改成栈就可以了,我下面也给出了代码。
-```C++
+```CPP
class Solution {
public:
bool isSymmetric(TreeNode* root) {
@@ -251,6 +251,8 @@ public:
# 相关题目推荐
+这两道题目基本和本题是一样的,只要稍加修改就可以AC。
+
* 100.相同的树
* 572.另一个树的子树
@@ -579,4 +581,4 @@ var isSymmetric = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md
index 8be3ac47..a2fd6f03 100644
--- a/problems/0102.二叉树的层序遍历.md
+++ b/problems/0102.二叉树的层序遍历.md
@@ -6,11 +6,8 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-# 二叉树的层序遍历
-看完这篇文章虽然不能打十个,但是可以迅速打八个!而且够快!
-
-学会二叉树的层序遍历,可以一口气撸完leetcode上八道题目:
+学会二叉树的层序遍历,可以一口气打完以下十题:
* 102.二叉树的层序遍历
* 107.二叉树的层次遍历II
@@ -20,11 +17,18 @@
* 515.在每个树行中找最大值
* 116.填充每个节点的下一个右侧节点指针
* 117.填充每个节点的下一个右侧节点指针II
+* 104.二叉树的最大深度
+* 111.二叉树的最小深度
+
+在之前写过这篇文章 [二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html),可惜当时只打了5个,还不够,再给我一次机会,我打十个!
+
+
-## 102.二叉树的层序遍历
-题目地址:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
+# 102.二叉树的层序遍历
+
+[力扣题目链接](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/)
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
@@ -34,10 +38,9 @@
我们之前讲过了三篇关于二叉树的深度优先遍历的文章:
-* [二叉树:前中后序递归法](https://mp.weixin.qq.com/s/Ww60X5mIKWdMQV4cN3ejOA)
-* [二叉树:前中后序迭代法](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)
-* [二叉树:前中后序迭代方式统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)
-
+* [二叉树:前中后序递归法](https://programmercarl.com/二叉树的递归遍历.html)
+* [二叉树:前中后序迭代法](https://programmercarl.com/二叉树的迭代遍历.html)
+* [二叉树:前中后序迭代方式统一写法](https://programmercarl.com/二叉树的统一迭代法.html)
接下来我们再来介绍二叉树的另一种遍历方式:层序遍历。
@@ -53,11 +56,11 @@
这样就实现了层序从左到右遍历二叉树。
-代码如下:**这份代码也可以作为二叉树层序遍历的模板,以后再打七个就靠它了**。
+代码如下:**这份代码也可以作为二叉树层序遍历的模板,打十个就靠它了**。
C++代码:
-```
+```CPP
class Solution {
public:
vector> levelOrder(TreeNode* root) {
@@ -84,28 +87,48 @@ public:
python代码:
+
+```python3
+
+class Solution:
+ """二叉树层序遍历迭代解法"""
+
+ def levelOrder(self, root: TreeNode) -> List[List[int]]:
+ results = []
+ if not root:
+ return results
+
+ from collections import deque
+ que = deque([root])
+
+ while que:
+ size = len(que)
+ result = []
+ for _ in range(size):
+ cur = que.popleft()
+ result.append(cur.val)
+ if cur.left:
+ que.append(cur.left)
+ if cur.right:
+ que.append(cur.right)
+ results.append(result)
+
+ return results
+```
```python
+# 递归法
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
- if not root:
- return []
-
- quene = [root]
- out_list = []
-
- while quene:
- length = len(queue)
- in_list = []
- for _ in range(length):
- curnode = queue.pop(0) # (默认移除列表最后一个元素)这里需要移除队列最头上的那个
- in_list.append(curnode.val)
- if curnode.left: queue.append(curnode.left)
- if curnode.right: queue.append(curnode.right)
- out_list.append(in_list)
-
- return out_list
+ res = []
+ def helper(root, depth):
+ if not root: return []
+ if len(res) == depth: res.append([]) # start the current depth
+ res[depth].append(root.val) # fulfil the current depth
+ if root.left: helper(root.left, depth + 1) # process child nodes for the next depth
+ if root.right: helper(root.right, depth + 1)
+ helper(root, 0)
+ return res
```
-
java:
```Java
@@ -225,11 +248,12 @@ var levelOrder = function(root) {
```
-**此时我们就掌握了二叉树的层序遍历了,那么如下五道leetcode上的题目,只需要修改模板的一两行代码(不能再多了),便可打倒!**
+**此时我们就掌握了二叉树的层序遍历了,那么如下九道力扣上的题目,只需要修改模板的两三行代码(不能再多了),便可打倒!**
-## 107.二叉树的层次遍历 II
-题目链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/
+# 107.二叉树的层次遍历 II
+
+[力扣题目链接](https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/)
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
@@ -241,7 +265,7 @@ var levelOrder = function(root) {
C++代码:
-```C++
+```CPP
class Solution {
public:
vector> levelOrderBottom(TreeNode* root) {
@@ -270,29 +294,29 @@ python代码:
```python
class Solution:
+ """二叉树层序遍历II迭代解法"""
+
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
+ results = []
if not root:
- return []
- quene = [root]
- out_list = []
+ return results
- while quene:
- in_list = []
- for _ in range(len(quene)):
- node = quene.pop(0)
- in_list.append(node.val)
- if node.left:
- quene.append(node.left)
- if node.right:
- quene.append(node.right)
-
- out_list.append(in_list)
-
- out_list.reverse()
- return out_list
-
-# 执行用时:36 ms, 在所有 Python3 提交中击败了92.00%的用户
-# 内存消耗:15.2 MB, 在所有 Python3 提交中击败了63.76%的用户
+ from collections import deque
+ que = deque([root])
+
+ while que:
+ result = []
+ for _ in range(len(que)):
+ cur = que.popleft()
+ result.append(cur.val)
+ if cur.left:
+ que.append(cur.left)
+ if cur.right:
+ que.append(cur.right)
+ results.append(result)
+
+ results.reverse()
+ return results
```
Java:
@@ -404,9 +428,9 @@ var levelOrderBottom = function(root) {
```
-## 199.二叉树的右视图
+# 199.二叉树的右视图
-题目链接:https://leetcode-cn.com/problems/binary-tree-right-side-view/
+[力扣题目链接](https://leetcode-cn.com/problems/binary-tree-right-side-view/)
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
@@ -418,7 +442,7 @@ var levelOrderBottom = function(root) {
C++代码:
-```C++
+```CPP
class Solution {
public:
vector rightSideView(TreeNode* root) {
@@ -581,9 +605,9 @@ var rightSideView = function(root) {
};
```
-## 637.二叉树的层平均值
+# 637.二叉树的层平均值
-题目链接:https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/)
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
@@ -595,7 +619,7 @@ var rightSideView = function(root) {
C++代码:
-```C++
+```CPP
class Solution {
public:
vector averageOfLevels(TreeNode* root) {
@@ -624,32 +648,29 @@ python代码:
```python
class Solution:
+ """二叉树层平均值迭代解法"""
+
def averageOfLevels(self, root: TreeNode) -> List[float]:
+ results = []
if not root:
- return []
+ return results
- quene = deque([root])
- out_list = []
-
- while quene:
- in_list = []
-
- for _ in range(len(quene)):
- node = quene.popleft()
- in_list.append(node.val)
- if node.left:
- quene.append(node.left)
- if node.right:
- quene.append(node.right)
-
- out_list.append(in_list)
-
- out_list = map(lambda x: sum(x) / len(x), out_list)
-
- return out_list
+ from collections import deque
+ que = deque([root])
-# 执行用时:56 ms, 在所有 Python3 提交中击败了81.48%的用户
-# 内存消耗:17 MB, 在所有 Python3 提交中击败了89.68%的用户
+ while que:
+ size = len(que)
+ sum_ = 0
+ for _ in range(size):
+ cur = que.popleft()
+ sum_ += cur.val
+ if cur.left:
+ que.append(cur.left)
+ if cur.right:
+ que.append(cur.right)
+ results.append(sum_ / size)
+
+ return results
```
java:
@@ -765,9 +786,9 @@ var averageOfLevels = function(root) {
};
```
-## 429.N叉树的层序遍历
+# 429.N叉树的层序遍历
-题目链接:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/
+[力扣题目链接](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/)
给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
@@ -790,7 +811,7 @@ var averageOfLevels = function(root) {
C++代码:
-```C++
+```CPP
class Solution {
public:
vector> levelOrder(Node* root) {
@@ -819,52 +840,28 @@ public:
python代码:
```python
-
class Solution:
+ """N叉树的层序遍历迭代法"""
+
def levelOrder(self, root: 'Node') -> List[List[int]]:
+ results = []
if not root:
- return []
+ return results
- quene = deque([root])
- out_list = []
-
- while quene:
- in_list = []
-
- for _ in range(len(quene)):
- node = quene.popleft()
- in_list.append(node.val)
- if node.children:
- # 这个地方要用extend而不是append,我们看下面的例子:
- # In [18]: alist=[]
- # In [19]: alist.append([1,2,3])
- # In [20]: alist
- # Out[20]: [[1, 2, 3]]
- # In [21]: alist.extend([4,5,6])
- # In [22]: alist
- # Out[22]: [[1, 2, 3], 4, 5, 6]
- # 可以看到extend对要添加的list进行了一个解包操作
- # print(root.children),可以得到children是一个包含
- # 孩子节点地址的list,我们使用for遍历quene的时候,
- # 希望quene是一个单层list,所以要用extend
- # 使用extend的情况,如果print(quene),结果是
- # deque([<__main__.Node object at 0x7f60763ae0a0>])
- # deque([<__main__.Node object at 0x7f607636e6d0>, <__main__.Node object at 0x7f607636e130>, <__main__.Node object at 0x7f607636e310>])
- # deque([<__main__.Node object at 0x7f607636e880>, <__main__.Node object at 0x7f607636ef10>])
- # 可以看到是单层list
- # 如果使用append,print(quene)的结果是
- # deque([<__main__.Node object at 0x7f18907530a0>])
- # deque([[<__main__.Node object at 0x7f18907136d0>, <__main__.Node object at 0x7f1890713130>, <__main__.Node object at 0x7f1890713310>]])
- # 可以看到是两层list,这样for的遍历就会报错
-
- quene.extend(node.children)
-
- out_list.append(in_list)
+ from collections import deque
+ que = deque([root])
- return out_list
-
-# 执行用时:60 ms, 在所有 Python3 提交中击败了76.99%的用户
-# 内存消耗:16.5 MB, 在所有 Python3 提交中击败了89.19%的用户
+ while que:
+ result = []
+ for _ in range(len(que)):
+ cur = que.popleft()
+ result.append(cur.val)
+ # cur.children 是 Node 对象组成的列表,也可能为 None
+ if cur.children:
+ que.extend(cur.children)
+ results.append(result)
+
+ return results
```
java:
@@ -985,9 +982,9 @@ var levelOrder = function(root) {
};
```
-## 515.在每个树行中找最大值
+# 515.在每个树行中找最大值
-题目链接:https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/
+[力扣题目链接](https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/)
您需要在二叉树的每一行中找到最大的值。
@@ -999,7 +996,7 @@ var levelOrder = function(root) {
C++代码:
-```C++
+```CPP
class Solution {
public:
vector largestValues(TreeNode* root) {
@@ -1042,6 +1039,31 @@ class Solution:
out_list.append(max(in_list))
return out_list
```
+java代码:
+
+```java
+class Solution {
+ public List largestValues(TreeNode root) {
+ List retVal = new ArrayList();
+ Queue tmpQueue = new LinkedList();
+ if (root != null) tmpQueue.add(root);
+
+ while (tmpQueue.size() != 0){
+ int size = tmpQueue.size();
+ List lvlVals = new ArrayList();
+ for (int index = 0; index < size; index++){
+ TreeNode node = tmpQueue.poll();
+ lvlVals.add(node.val);
+ if (node.left != null) tmpQueue.add(node.left);
+ if (node.right != null) tmpQueue.add(node.right);
+ }
+ retVal.add(Collections.max(lvlVals));
+ }
+
+ return retVal;
+ }
+}
+```
go:
@@ -1100,7 +1122,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();
@@ -1115,9 +1137,9 @@ var largestValues = function(root) {
};
```
-## 116.填充每个节点的下一个右侧节点指针
+# 116.填充每个节点的下一个右侧节点指针
-题目链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
+[力扣题目链接](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/)
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
@@ -1143,7 +1165,7 @@ struct Node {
C++代码:
-```C++
+```CPP
class Solution {
public:
Node* connect(Node* root) {
@@ -1176,6 +1198,35 @@ public:
};
```
+java代码:
+
+```java
+class Solution {
+ public Node connect(Node root) {
+ Queue tmpQueue = new LinkedList();
+ if (root != null) tmpQueue.add(root);
+
+ while (tmpQueue.size() != 0){
+ int size = tmpQueue.size();
+
+ Node cur = tmpQueue.poll();
+ if (cur.left != null) tmpQueue.add(cur.left);
+ if (cur.right != null) tmpQueue.add(cur.right);
+
+ for (int index = 1; index < size; index++){
+ Node next = tmpQueue.poll();
+ if (next.left != null) tmpQueue.add(next.left);
+ if (next.right != null) tmpQueue.add(next.right);
+
+ cur.next = next;
+ cur = next;
+ }
+ }
+
+ return root;
+ }
+}
+```
python代码:
@@ -1254,9 +1305,9 @@ func connect(root *Node) *Node {
}
```
-## 117.填充每个节点的下一个右侧节点指针II
+# 117.填充每个节点的下一个右侧节点指针II
-题目地址:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/)
思路:
@@ -1264,7 +1315,7 @@ func connect(root *Node) *Node {
C++代码:
-```C++
+```CPP
class Solution {
public:
Node* connect(Node* root) {
@@ -1295,6 +1346,44 @@ public:
}
};
```
+
+Java 代码:
+
+```java
+// 二叉树之层次遍历
+class Solution {
+ public Node connect(Node root) {
+ Queue queue = new LinkedList<>();
+ if (root != null) {
+ queue.add(root);
+ }
+ while (!queue.isEmpty()) {
+ int size = queue.size();
+ Node node = null;
+ Node nodePre = null;
+
+ for (int i = 0; i < size; i++) {
+ if (i == 0) {
+ nodePre = queue.poll(); // 取出本层头一个节点
+ node = nodePre;
+ } else {
+ node = queue.poll();
+ nodePre.next = node; // 本层前一个节点 next 指向当前节点
+ nodePre = nodePre.next;
+ }
+ if (node.left != null) {
+ queue.add(node.left);
+ }
+ if (node.right != null) {
+ queue.add(node.right);
+ }
+ }
+ nodePre.next = null; // 本层最后一个节点 next 指向 null
+ }
+ return root;
+ }
+}
+```
python代码:
```python
@@ -1378,12 +1467,297 @@ func connect(root *Node) *Node {
return root
}
```
+# 104.二叉树的最大深度
-## 总结
+[力扣题目链接](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)
-二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现(此时是不是又发现队列的应用了)。
+给定一个二叉树,找出其最大深度。
-虽然不能一口气打十个,打八个也还行。
+二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
+
+说明: 叶子节点是指没有子节点的节点。
+
+示例:
+
+给定二叉树 [3,9,20,null,null,15,7],
+
+
+
+返回它的最大深度 3 。
+
+思路:
+
+使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
+
+在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:
+
+
+
+所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
+
+C++代码如下:
+
+```CPP
+class Solution {
+public:
+ int maxDepth(TreeNode* root) {
+ if (root == NULL) return 0;
+ int depth = 0;
+ queue que;
+ que.push(root);
+ while(!que.empty()) {
+ int size = que.size();
+ depth++; // 记录深度
+ for (int i = 0; i < size; i++) {
+ TreeNode* node = que.front();
+ que.pop();
+ if (node->left) que.push(node->left);
+ if (node->right) que.push(node->right);
+ }
+ }
+ return depth;
+ }
+};
+```
+
+Java:
+```Java
+class Solution {
+ public int maxDepth(TreeNode root) {
+ if (root == null) return 0;
+ Queue que = new LinkedList<>();
+ que.offer(root);
+ int depth = 0;
+ while (!que.isEmpty())
+ {
+ int len = que.size();
+ while (len > 0)
+ {
+ TreeNode node = que.poll();
+ if (node.left != null) que.offer(node.left);
+ if (node.right != null) que.offer(node.right);
+ len--;
+ }
+ depth++;
+ }
+ return depth;
+ }
+}
+```
+
+
+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
+/**
+ * Definition for a binary tree node.
+ * type TreeNode struct {
+ * Val int
+ * Left *TreeNode
+ * Right *TreeNode
+ * }
+ */
+func maxDepth(root *TreeNode) int {
+ ans:=0
+ if root==nil{
+ return 0
+ }
+ queue:=list.New()
+ queue.PushBack(root)
+ for queue.Len()>0{
+ length:=queue.Len()
+ for i:=0;i que;
+ que.push(root);
+ while(!que.empty()) {
+ int size = que.size();
+ depth++; // 记录最小深度
+ for (int i = 0; i < size; i++) {
+ TreeNode* node = que.front();
+ que.pop();
+ if (node->left) que.push(node->left);
+ if (node->right) que.push(node->right);
+ if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
+ return depth;
+ }
+ }
+ }
+ return depth;
+ }
+};
+```
+
+Java:
+```java
+class Solution {
+ public int minDepth(TreeNode root){
+ if (root == null) {
+ return 0;
+ }
+ Queue 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);
+ }
+ }
+ 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
+/**
+ * Definition for a binary tree node.
+ * type TreeNode struct {
+ * Val int
+ * Left *TreeNode
+ * Right *TreeNode
+ * }
+ */
+func minDepth(root *TreeNode) int {
+ ans:=0
+ if root==nil{
+ return 0
+ }
+ queue:=list.New()
+ queue.PushBack(root)
+ for queue.Len()>0{
+ length:=queue.Len()
+ for i:=0;i 二叉树的层序遍历(Javascript语言完全版) (迭代 + 递归)
-
-```js
-/**
- * 102. 二叉树的层序遍历
- * @param {TreeNode} root
- * @return {number[][]}
- */
-
-// 迭代
-
-var levelOrder = function(root) {
- const queue = [], res = [];
- root && queue.push(root);
- while(len = queue.length) {
- const val = [];
- while(len--) {
- const node = queue.shift();
- val.push(node.val);
- node.left && queue.push(node.left);
- node.right && queue.push(node.right);
- }
- res.push(val);
- }
- return res;
-};
-
-// 递归
-var levelOrder = function(root) {
- const res = [];
- function defs (root, i) {
- if(!root) return;
- if(!res[i]) res[i] = [];
- res[i].push(root.val)
- root.left && defs(root.left, i + 1);
- root.right && defs(root.right, i + 1);
- }
- defs(root, 0);
- return res;
-};
-
-
-/**
- * 107. 二叉树的层序遍历 II
- * @param {TreeNode} root
- * @return {number[][]}
- */
-
-// 迭代
-
-var levelOrderBottom = function(root) {
- const queue = [], res = [];
- root && queue.push(root);
- while(len = queue.length) {
- const val = [];
- while(len--) {
- const node = queue.shift();
- val.push(node.val);
- node.left && queue.push(node.left);
- node.right && queue.push(node.right);
- }
- res.push(val);
- }
- return res.reverse()
-};
-
-// 递归
-
-var levelOrderBottom = function(root) {
- const res = [];
- function defs (root, i) {
- if(!root) return;
- if(!res[i]) res[i] = [];
- res[i].push(root.val);
- root.left && defs(root.left, i + 1);
- root.right && defs(root.right, i + 1);
- }
- defs(root, 0);
- return res.reverse();
-};
-
-/**
- * 199. 二叉树的右视图
- * @param {TreeNode} root
- * @return {number[]}
- */
-
-// 迭代
-
-var rightSideView = function(root) {
- const res = [], queue = [];
- root && queue.push(root);
- while(l = queue.length) {
- while (l--) {
- const {val, left, right} = queue.shift();
- !l && res.push(val);
- left && queue.push(left);
- right && queue.push(right);
- }
- }
- return res;
-};
-
-// 递归
-var rightSideView = function(root) {
- const res = [];
- function defs(root, i) {
- if(!root) return;
- res[i] = root.val;
- root.left && defs(root.left, i + 1);
- root.right && defs(root.right, i + 1);
- }
- defs(root, 0);
- return res;
-};
-
-/**
- * 637. 二叉树的层平均值
- * @param {TreeNode} root
- * @return {number[]}
- */
-
-// 迭代
-var averageOfLevels = function(root) {
- const queue = [], res = [];
- root && queue.push(root);
- while(len = queue.length) {
- let sum = 0, l = len;
- while(l--) {
- const {val, left, right} = queue.shift();
- sum += val;
- left && queue.push(left);
- right && queue.push(right);
- }
- res.push(sum/len);
- }
- return res;
-};
-
-// 递归
-var averageOfLevels = function(root) {
- const resCount = [], res = [];
- function defs(root, i) {
- if(!root) return;
- if(isNaN(res[i])) resCount[i] = res[i] = 0;
- res[i] += root.val;
- resCount[i]++;
- root.left && defs(root.left, i + 1);
- root.right && defs(root.right, i + 1);
- }
- defs(root, 0);
- return res.map((val, i) => val / resCount[i]);
-};
-
-/**
- * 515. 在每个树行中找最大值
- * @param {TreeNode} root
- * @return {number[]}
- */
-
-// 迭代
-const MIN_G = Number.MIN_SAFE_INTEGER;
-var largestValues = function(root) {
- const queue = [], res = [];
- root && queue.push(root);
- while(len = queue.length) {
- let max = MIN_G;
- while(len--) {
- const {val, left, right} = queue.shift();
- max = max > val ? max : val;
- left && queue.push(left);
- right && queue.push(right);
- }
- res.push(max);
- }
- return res;
-};
-
-// 递归
-var largestValues = function(root) {
- const res = [];
- function defs (root, i) {
- if(!root) return;
- if(isNaN(res[i])) res[i] = root.val;
- res[i] = res[i] > root.val ? res[i] : root.val;
- root.left && defs(root.left, i + 1);
- root.right && defs(root.right, i + 1);
- }
- defs(root, 0);
- return res;
-};
-
-/**
- * 429. N 叉树的层序遍历
- * @param {Node|null} root
- * @return {number[][]}
- */
-
-// 迭代
-var levelOrder = function(root) {
- const queue = [], res = [];
- root && queue.push(root);
- while(len = queue.length) {
- const vals = [];
- while(len--) {
- const {val, children} = queue.shift();
- vals.push(val);
- for(const e of children) {
- queue.push(e);
- }
- }
- res.push(vals);
- }
- return res;
-};
-
-// 递归
-
-var levelOrder = function(root) {
- const res = [];
- function defs (root, i) {
- if(!root) return;
- if(!res[i]) res[i] = [];
- res[i].push(root.val);
- for(const e of root.children) {
- defs(e, i + 1);
- }
- }
- defs(root, 0);
- return res;
-};
-
-/**
- * 116. 填充每个节点的下一个右侧节点指针
- * 117. 填充每个节点的下一个右侧节点指针 II
- * @param {Node} root
- * @return {Node}
- */
-
-// 迭代
-var connect = function(root) {
- const queue = [];
- root && queue.push(root);
- while(len = queue.length) {
- while(len--) {
- const node1 = queue.shift(),
- node2 = len ? queue[0] : null;
- node1.next = node2;
- node1.left && queue.push(node1.left);
- node1.right && queue.push(node1.right);
- }
- }
- return root;
-};
-
-// 递归
-var connect = function(root) {
- const res = [];
- function defs (root, i) {
- if(!root) return;
- if(res[i]) res[i].next = root;
- res[i] = root;
- root.left && defs(root.left, i + 1);
- root.right && defs(root.right, i + 1);
- }
- defs(root, 0);
- return root;
-};
-```
-
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0104.二叉树的最大深度.md b/problems/0104.二叉树的最大深度.md
index 218a966c..cde3d460 100644
--- a/problems/0104.二叉树的最大深度.md
+++ b/problems/0104.二叉树的最大深度.md
@@ -8,12 +8,13 @@
看完本篇可以一起做了如下两道题目:
+
* 104.二叉树的最大深度
-* 559.N叉树的最大深度
+* 559.n叉树的最大深度
-## 104.二叉树的最大深度
+# 104.二叉树的最大深度
-题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)
给定一个二叉树,找出其最大深度。
@@ -28,7 +29,7 @@
返回它的最大深度 3 。
-### 递归法
+## 递归法
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
@@ -41,53 +42,53 @@
1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
代码如下:
-```
-int getDepth(TreeNode* node)
+```c++
+int getdepth(treenode* node)
```
2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
代码如下:
-```
-if (node == NULL) return 0;
+```c++
+if (node == null) return 0;
```
3. 确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
代码如下:
-```
-int leftDepth = getDepth(node->left); // 左
-int rightDepth = getDepth(node->right); // 右
-int depth = 1 + max(leftDepth, rightDepth); // 中
+```c++
+int leftdepth = getdepth(node->left); // 左
+int rightdepth = getdepth(node->right); // 右
+int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
```
-所以整体C++代码如下:
+所以整体c++代码如下:
-```C++
-class Solution {
+```c++
+class solution {
public:
- int getDepth(TreeNode* node) {
- if (node == NULL) return 0;
- int leftDepth = getDepth(node->left); // 左
- int rightDepth = getDepth(node->right); // 右
- int depth = 1 + max(leftDepth, rightDepth); // 中
+ int getdepth(treenode* node) {
+ if (node == null) return 0;
+ int leftdepth = getdepth(node->left); // 左
+ int rightdepth = getdepth(node->right); // 右
+ int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
}
- int maxDepth(TreeNode* root) {
- return getDepth(root);
+ int maxdepth(treenode* root) {
+ return getdepth(root);
}
};
```
-代码精简之后C++代码如下:
-```C++
-class Solution {
+代码精简之后c++代码如下:
+```c++
+class solution {
public:
- int maxDepth(TreeNode* root) {
- if (root == NULL) return 0;
- return 1 + max(maxDepth(root->left), maxDepth(root->right));
+ int maxdepth(treenode* root) {
+ if (root == null) return 0;
+ return 1 + max(maxdepth(root->left), maxdepth(root->right));
}
};
@@ -98,65 +99,65 @@ public:
本题当然也可以使用前序,代码如下:(**充分表现出求深度回溯的过程**)
-```C++
-class Solution {
+```c++
+class solution {
public:
int result;
- void getDepth(TreeNode* node, int depth) {
+ void getdepth(treenode* node, int depth) {
result = depth > result ? depth : result; // 中
- if (node->left == NULL && node->right == NULL) return ;
+ if (node->left == null && node->right == null) return ;
if (node->left) { // 左
depth++; // 深度+1
- getDepth(node->left, depth);
+ getdepth(node->left, depth);
depth--; // 回溯,深度-1
}
if (node->right) { // 右
depth++; // 深度+1
- getDepth(node->right, depth);
+ getdepth(node->right, depth);
depth--; // 回溯,深度-1
}
return ;
}
- int maxDepth(TreeNode* root) {
+ int maxdepth(treenode* root) {
result = 0;
if (root == 0) return result;
- getDepth(root, 1);
- return result;
- }
-};
-```
-
-**可以看出使用了前序(中左右)的遍历顺序,这才是真正求深度的逻辑!**
-
-注意以上代码是为了把细节体现出来,简化一下代码如下:
-
-```C++
-class Solution {
-public:
- int result;
- void getDepth(TreeNode* node, int depth) {
- result = depth > result ? depth : result; // 中
- if (node->left == NULL && node->right == NULL) return ;
- if (node->left) { // 左
- getDepth(node->left, depth + 1);
- }
- if (node->right) { // 右
- getDepth(node->right, depth + 1);
- }
- return ;
- }
- int maxDepth(TreeNode* root) {
- result = 0;
- if (root == 0) return result;
- getDepth(root, 1);
+ getdepth(root, 1);
return result;
}
};
```
-### 迭代法
+**可以看出使用了前序(中左右)的遍历顺序,这才是真正求深度的逻辑!**
+
+注意以上代码是为了把细节体现出来,简化一下代码如下:
+
+```c++
+class solution {
+public:
+ int result;
+ void getdepth(treenode* node, int depth) {
+ result = depth > result ? depth : result; // 中
+ if (node->left == null && node->right == null) return ;
+ if (node->left) { // 左
+ getdepth(node->left, depth + 1);
+ }
+ if (node->right) { // 右
+ getdepth(node->right, depth + 1);
+ }
+ return ;
+ }
+ int maxdepth(treenode* root) {
+ result = 0;
+ if (root == 0) return result;
+ getdepth(root, 1);
+ return result;
+ }
+};
+```
+
+## 迭代法
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
@@ -166,23 +167,23 @@ public:
所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
-如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
+如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)
-C++代码如下:
+c++代码如下:
-```C++
-class Solution {
+```c++
+class solution {
public:
- int maxDepth(TreeNode* root) {
- if (root == NULL) return 0;
+ int maxdepth(treenode* root) {
+ if (root == null) return 0;
int depth = 0;
- queue que;
+ queue que;
que.push(root);
while(!que.empty()) {
int size = que.size();
depth++; // 记录深度
for (int i = 0; i < size; i++) {
- TreeNode* node = que.front();
+ treenode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
@@ -193,19 +194,19 @@ public:
};
```
-那么我们可以顺便解决一下N叉树的最大深度问题
+那么我们可以顺便解决一下n叉树的最大深度问题
-## 559.N叉树的最大深度
+# 559.n叉树的最大深度
-题目地址:https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/)
-给定一个 N 叉树,找到其最大深度。
+给定一个 n 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
例如,给定一个 3叉树 :
-
+
我们应返回其最大深度,3。
@@ -213,39 +214,39 @@ public:
依然可以提供递归法和迭代法,来解决这个问题,思路是和二叉树思路一样的,直接给出代码如下:
-### 递归法
+## 递归法
-C++代码:
+c++代码:
-```C++
-class Solution {
+```c++
+class solution {
public:
- int maxDepth(Node* root) {
+ int maxdepth(node* root) {
if (root == 0) return 0;
int depth = 0;
for (int i = 0; i < root->children.size(); i++) {
- depth = max (depth, maxDepth(root->children[i]));
+ depth = max (depth, maxdepth(root->children[i]));
}
return depth + 1;
}
};
```
-### 迭代法
+## 迭代法
依然是层序遍历,代码如下:
-```C++
-class Solution {
+```c++
+class solution {
public:
- int maxDepth(Node* root) {
- queue que;
- if (root != NULL) que.push(root);
+ int maxdepth(node* root) {
+ queue que;
+ if (root != null) que.push(root);
int depth = 0;
while (!que.empty()) {
int size = que.size();
depth++; // 记录深度
for (int i = 0; i < size; i++) {
- Node* node = que.front();
+ node* node = que.front();
que.pop();
for (int j = 0; j < node->children.size(); j++) {
if (node->children[j]) que.push(node->children[j]);
@@ -257,45 +258,46 @@ public:
};
```
-## 其他语言版本
+# 其他语言版本
+## java
-Java:
+### 104.二叉树的最大深度
-```Java
-class Solution {
+```java
+class solution {
/**
* 递归法
*/
- public int maxDepth(TreeNode root) {
+ public int maxdepth(treenode root) {
if (root == null) {
return 0;
}
- int leftDepth = maxDepth(root.left);
- int rightDepth = maxDepth(root.right);
- return Math.max(leftDepth, rightDepth) + 1;
+ int leftdepth = maxdepth(root.left);
+ int rightdepth = maxdepth(root.right);
+ return math.max(leftdepth, rightdepth) + 1;
}
}
```
-```Java
-class Solution {
+```java
+class solution {
/**
* 迭代法,使用层序遍历
*/
- public int maxDepth(TreeNode root) {
+ public int maxdepth(treenode root) {
if(root == null) {
return 0;
}
- Deque deque = new LinkedList<>();
+ deque deque = new linkedlist<>();
deque.offer(root);
int depth = 0;
- while (!deque.isEmpty()) {
+ while (!deque.isempty()) {
int size = deque.size();
depth++;
for (int i = 0; i < size; i++) {
- TreeNode poll = deque.poll();
+ treenode poll = deque.poll();
if (poll.left != null) {
deque.offer(poll.left);
}
@@ -309,37 +311,68 @@ class Solution {
}
```
-Python:
+### 559.n叉树的最大深度
+```java
+class solution {
+ /**
+ * 迭代法,使用层序遍历
+ */
+ public int maxDepth(Node root) {
+ if (root == null) return 0;
+ int depth = 0;
+ Queue que = new LinkedList<>();
+ que.offer(root);
+ while (!que.isEmpty())
+ {
+ depth ++;
+ int len = que.size();
+ while (len > 0)
+ {
+ Node node = que.poll();
+ for (int i = 0; i < node.children.size(); i++)
+ if (node.children.get(i) != null)
+ que.offer(node.children.get(i));
+ len--;
+ }
+ }
+ return depth;
+ }
+}
+```
-104.二叉树的最大深度
-> 递归法:
+## python
+
+### 104.二叉树的最大深度
+
+递归法:
```python
-class Solution:
- def maxDepth(self, root: TreeNode) -> int:
- return self.getDepth(root)
+class solution:
+ def maxdepth(self, root: treenode) -> int:
+ return self.getdepth(root)
- def getDepth(self, node):
+ def getdepth(self, node):
if not node:
return 0
- leftDepth = self.getDepth(node.left) #左
- rightDepth = self.getDepth(node.right) #右
- depth = 1 + max(leftDepth, rightDepth) #中
+ leftdepth = self.getdepth(node.left) #左
+ rightdepth = self.getdepth(node.right) #右
+ depth = 1 + max(leftdepth, rightdepth) #中
return depth
```
-> 递归法;精简代码
+
+递归法:精简代码
```python
-class Solution:
- def maxDepth(self, root: TreeNode) -> int:
+class solution:
+ def maxdepth(self, root: treenode) -> int:
if not root:
return 0
- return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
+ return 1 + max(self.maxdepth(root.left), self.maxdepth(root.right))
```
-> 迭代法:
+迭代法:
```python
import collections
-class Solution:
- def maxDepth(self, root: TreeNode) -> int:
+class solution:
+ def maxdepth(self, root: treenode) -> int:
if not root:
return 0
depth = 0 #记录深度
@@ -357,24 +390,25 @@ class Solution:
return depth
```
-559.N叉树的最大深度
-> 递归法:
+### 559.n叉树的最大深度
+
+递归法:
```python
-class Solution:
- def maxDepth(self, root: 'Node') -> int:
+class solution:
+ def maxdepth(self, root: 'node') -> int:
if not root:
return 0
depth = 0
for i in range(len(root.children)):
- depth = max(depth, self.maxDepth(root.children[i]))
+ depth = max(depth, self.maxdepth(root.children[i]))
return depth + 1
```
-> 迭代法:
+迭代法:
```python
import collections
-class Solution:
- def maxDepth(self, root: 'Node') -> int:
+class solution:
+ def maxdepth(self, root: 'node') -> int:
queue = collections.deque()
if root:
queue.append(root)
@@ -390,10 +424,10 @@ class Solution:
return depth
```
-> 使用栈来模拟后序遍历依然可以
+使用栈来模拟后序遍历依然可以
```python
-class Solution:
- def maxDepth(self, root: 'Node') -> int:
+class solution:
+ def maxdepth(self, root: 'node') -> int:
st = []
if root:
st.append(root)
@@ -401,9 +435,9 @@ class Solution:
result = 0
while st:
node = st.pop()
- if node != None:
+ if node != none:
st.append(node) #中
- st.append(None)
+ st.append(none)
depth += 1
for i in range(len(node.children)): #处理孩子
if node.children[i]:
@@ -417,15 +451,15 @@ class Solution:
```
-Go:
+## go
```go
/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
+ * definition for a binary tree node.
+ * type treenode struct {
+ * val int
+ * left *treenode
+ * right *treenode
* }
*/
func max (a, b int) int {
@@ -435,28 +469,28 @@ func max (a, b int) int {
return b;
}
// 递归
-func maxDepth(root *TreeNode) int {
+func maxdepth(root *treenode) int {
if root == nil {
return 0;
}
- return max(maxDepth(root.Left), maxDepth(root.Right)) + 1;
+ return max(maxdepth(root.left), maxdepth(root.right)) + 1;
}
// 遍历
-func maxDepth(root *TreeNode) int {
+func maxdepth(root *treenode) int {
levl := 0;
- queue := make([]*TreeNode, 0);
+ queue := make([]*treenode, 0);
if root != nil {
queue = append(queue, root);
}
for l := len(queue); l > 0; {
for ;l > 0;l-- {
node := queue[0];
- if node.Left != nil {
- queue = append(queue, node.Left);
+ if node.left != nil {
+ queue = append(queue, node.left);
}
- if node.Right != nil {
- queue = append(queue, node.Right);
+ if node.right != nil {
+ queue = append(queue, node.right);
}
queue = queue[1:];
}
@@ -469,49 +503,82 @@ func maxDepth(root *TreeNode) int {
```
-JavaScript
+## javascript
+
```javascript
-var maxDepth = function(root) {
+var maxdepth = function(root) {
if (!root) return root
- return 1 + Math.max(maxDepth(root.left), maxDepth(root.right))
+ return 1 + math.max(maxdepth(root.left), maxdepth(root.right))
};
```
+
二叉树最大深度递归遍历
```javascript
-var maxDepth = function(root) {
+var maxdepth = function(root) {
//使用递归的方法 递归三部曲
//1. 确定递归函数的参数和返回值
- const getDepth=function(node){
+ const getdepth=function(node){
//2. 确定终止条件
if(node===null){
return 0;
}
//3. 确定单层逻辑
- let leftDepth=getDepth(node.left);
- let rightDepth=getDepth(node.right);
- let depth=1+Math.max(leftDepth,rightDepth);
+ let leftdepth=getdepth(node.left);
+ let rightdepth=getdepth(node.right);
+ let depth=1+math.max(leftdepth,rightdepth);
return depth;
}
- return getDepth(root);
+ return getdepth(root);
};
```
+
二叉树最大深度层级遍历
```javascript
var maxDepth = function(root) {
- //使用递归的方法 递归三部曲
- //1. 确定递归函数的参数和返回值
- const getDepth=function(node){
- //2. 确定终止条件
- if(node===null){
- return 0;
+ if(!root) return 0
+ let count = 0
+ const queue = [root]
+ while(queue.length) {
+ let size = queue.length
+ /* 层数+1 */
+ count++
+ while(size--) {
+ let node = queue.shift();
+ node.left && queue.push(node.left);
+ node.right && queue.push(node.right);
}
- //3. 确定单层逻辑
- let leftDepth=getDepth(node.left);
- let rightDepth=getDepth(node.right);
- let depth=1+Math.max(leftDepth,rightDepth);
- return depth;
}
- return getDepth(root);
+ return count
+};
+```
+
+N叉树的最大深度 递归写法
+```js
+var maxDepth = function(root) {
+ if(!root) return 0
+ let depth = 0
+ for(let node of root.children) {
+ depth = Math.max(depth, maxDepth(node))
+ }
+ return depth + 1
+}
+```
+
+N叉树的最大深度 层序遍历
+```js
+var maxDepth = function(root) {
+ if(!root) return 0
+ let count = 0
+ let queue = [root]
+ while(queue.length) {
+ let size = queue.length
+ count++
+ while(size--) {
+ let node = queue.shift()
+ node && (queue = [...queue, ...node.children])
+ }
+ }
+ return count
};
```
@@ -519,4 +586,4 @@ var maxDepth = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0106.从中序与后序遍历序列构造二叉树.md b/problems/0106.从中序与后序遍历序列构造二叉树.md
index 4c5a70a0..a9639d8f 100644
--- a/problems/0106.从中序与后序遍历序列构造二叉树.md
+++ b/problems/0106.从中序与后序遍历序列构造二叉树.md
@@ -12,9 +12,9 @@
* 106.从中序与后序遍历序列构造二叉树
* 105.从前序与中序遍历序列构造二叉树
-## 106.从中序与后序遍历序列构造二叉树
+# 106.从中序与后序遍历序列构造二叉树
-题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
+[力扣题目链接](https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/)
根据一棵树的中序遍历与后序遍历构造二叉树。
@@ -29,7 +29,7 @@

-### 思路
+## 思路
首先回忆一下如何根据两个顺序构造一个唯一的二叉树,相信理论知识大家应该都清楚,就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来在切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。
@@ -59,7 +59,7 @@
不难写出如下代码:(先把框架写出来)
-```C++
+```CPP
TreeNode* traversal (vector& inorder, vector& postorder) {
// 第一步
@@ -95,7 +95,7 @@ TreeNode* traversal (vector& inorder, vector& postorder) {
**在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭又闭,必然乱套!**
-我在[数组:每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)和[数组:这个循环可以转懵很多人!](https://mp.weixin.qq.com/s/KTPhaeqxbMK9CxHUUgFDmg)中都强调过循环不变量的重要性,在二分查找以及螺旋矩阵的求解中,坚持循环不变量非常重要,本题也是。
+我在[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0035.搜索插入位置.html)和[数组:这个循环可以转懵很多人!](https://programmercarl.com/0059.螺旋矩阵II.html)中都强调过循环不变量的重要性,在二分查找以及螺旋矩阵的求解中,坚持循环不变量非常重要,本题也是。
首先要切割中序数组,为什么先切割中序数组呢?
@@ -105,7 +105,7 @@ TreeNode* traversal (vector& inorder, vector& postorder) {
中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割,如下代码中我坚持左闭右开的原则:
-```
+```C++
// 找到中序遍历的切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
@@ -155,7 +155,7 @@ root->right = traversal(rightInorder, rightPostorder);
### C++完整代码
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal (vector& inorder, vector& postorder) {
@@ -209,7 +209,7 @@ public:
加了日志的代码如下:(加了日志的代码不要在leetcode上提交,容易超时)
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal (vector& inorder, vector& postorder) {
@@ -277,7 +277,7 @@ public:
下面给出用下表索引写出的代码版本:(思路是一样的,只不过不用重复定义vector了,每次用下表索引来分割)
### C++优化版本
-```C++
+```CPP
class Solution {
private:
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
@@ -325,7 +325,7 @@ public:
那么这个版本写出来依然要打日志进行调试,打日志的版本如下:(**该版本不要在leetcode上提交,容易超时**)
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal (vector& inorder, int inorderBegin, int inorderEnd, vector& postorder, int postorderBegin, int postorderEnd) {
@@ -394,9 +394,9 @@ public:
};
```
-## 105.从前序与中序遍历序列构造二叉树
+# 105.从前序与中序遍历序列构造二叉树
-题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
+[力扣题目链接](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
根据一棵树的前序遍历与中序遍历构造二叉树。
@@ -411,7 +411,7 @@ public:

-### 思路
+## 思路
本题和106是一样的道理。
@@ -419,7 +419,7 @@ public:
带日志的版本C++代码如下: (**带日志的版本仅用于调试,不要在leetcode上提交,会超时**)
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal (vector& inorder, int inorderBegin, int inorderEnd, vector& preorder, int preorderBegin, int preorderEnd) {
@@ -493,7 +493,7 @@ public:
105.从前序与中序遍历序列构造二叉树,最后版本,C++代码:
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal (vector& inorder, int inorderBegin, int inorderEnd, vector& preorder, int preorderBegin, int preorderEnd) {
@@ -540,7 +540,7 @@ public:
};
```
-## 思考题
+# 思考题
前序和中序可以唯一确定一颗二叉树。
@@ -562,7 +562,7 @@ tree2 的前序遍历是[1 2 3], 后序遍历是[3 2 1]。
所以前序和后序不能唯一确定一颗二叉树!
-## 总结
+# 总结
之前我们讲的二叉树题目都是各种遍历二叉树,这次开始构造二叉树了,思路其实比较简单,但是真正代码实现出来并不容易。
@@ -578,9 +578,9 @@ tree2 的前序遍历是[1 2 3], 后序遍历是[3 2 1]。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
106.从中序与后序遍历序列构造二叉树
@@ -607,6 +607,7 @@ class Solution {
for (int i = inLeft; i < inRight; i++) {
if (inorder[i] == rootVal) {
rootIndex = i;
+ break;
}
}
// 根据rootIndex划分左右子树
@@ -653,47 +654,75 @@ class Solution {
}
```
-Python:
+## Python
+
105.从前序与中序遍历序列构造二叉树
```python
-# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
- if not preorder: return None //特殊情况
- root = TreeNode(preorder[0]) //新建父节点
- p=inorder.index(preorder[0]) //找到父节点在中序遍历的位置(因为没有重复的元素,才可以这样找)
- root.left = self.buildTree(preorder[1:p+1],inorder[:p]) //注意左节点时分割中序数组和前续数组的开闭环
- root.right = self.buildTree(preorder[p+1:],inorder[p+1:]) //分割中序数组和前续数组
- return root
-```
+ # 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
+ if not preorder:
+ return None
+
+ # 第二步: 前序遍历的第一个就是当前的中间节点.
+ root_val = preorder[0]
+ root = TreeNode(root_val)
+
+ # 第三步: 找切割点.
+ separator_idx = inorder.index(root_val)
+
+ # 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
+ inorder_left = inorder[:separator_idx]
+ inorder_right = inorder[separator_idx + 1:]
+
+ # 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
+ # ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的.
+ preorder_left = preorder[1:1 + len(inorder_left)]
+ preorder_right = preorder[1 + len(inorder_left):]
+
+ # 第六步: 递归
+ root.left = self.buildTree(preorder_left, inorder_left)
+ root.right = self.buildTree(preorder_right, inorder_right)
+
+ return root
+```
+
106.从中序与后序遍历序列构造二叉树
```python
-# 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 buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
- if not postorder: return None //特殊情况
- root = TreeNode(postorder[-1]) //新建父节点
- p=inorder.index(postorder[-1]) //找到父节点在中序遍历的位置*因为没有重复的元素,才可以这样找
- root.left = self.buildTree(inorder[:p],postorder[:p]) //分割中序数组和后续数组
- root.right = self.buildTree(inorder[p+1:],postorder[p:-1]) //注意右节点时分割中序数组和后续数组的开闭环
- return root
-```
-Go:
-> 106 从中序与后序遍历序列构造二叉树
+ # 第一步: 特殊情况讨论: 树为空. (递归终止条件)
+ if not postorder:
+ return None
+
+ # 第二步: 后序遍历的最后一个就是当前的中间节点.
+ root_val = postorder[-1]
+ root = TreeNode(root_val)
+
+ # 第三步: 找切割点.
+ separator_idx = inorder.index(root_val)
+
+ # 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
+ inorder_left = inorder[:separator_idx]
+ inorder_right = inorder[separator_idx + 1:]
+
+ # 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
+ # ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
+ postorder_left = postorder[:len(inorder_left)]
+ postorder_right = postorder[len(inorder_left): len(postorder) - 1]
+
+ # 第六步: 递归
+ root.left = self.buildTree(inorder_left, postorder_left)
+ root.right = self.buildTree(inorder_right, postorder_right)
+
+ return root
+```
+
+## Go
+
+106 从中序与后序遍历序列构造二叉树
```go
/**
@@ -726,7 +755,7 @@ func findRootIndex(inorder []int,target int) (index int){
}
```
-> 105 从前序与中序遍历序列构造二叉树
+105 从前序与中序遍历序列构造二叉树
```go
/**
@@ -759,7 +788,8 @@ func findRootIndex(target int,inorder []int) int{
-JavaScript
+## JavaScript
+
```javascript
var buildTree = function(inorder, postorder) {
if (!postorder.length) return null
@@ -793,4 +823,4 @@ var buildTree = function(preorder, inorder) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0108.将有序数组转换为二叉搜索树.md b/problems/0108.将有序数组转换为二叉搜索树.md
index 8ec5f3dc..f7bff27d 100644
--- a/problems/0108.将有序数组转换为二叉搜索树.md
+++ b/problems/0108.将有序数组转换为二叉搜索树.md
@@ -9,9 +9,9 @@
> 构造二叉搜索树,一不小心就平衡了
-## 108.将有序数组转换为二叉搜索树
+# 108.将有序数组转换为二叉搜索树
-题目链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/)
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
@@ -21,14 +21,14 @@

-## 思路
+# 思路
做这道题目之前大家可以了解一下这几道:
-* [106.从中序与后序遍历序列构造二叉树](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)
-* [654.最大二叉树](https://mp.weixin.qq.com/s/1iWJV6Aov23A7xCF4nV88w)中其实已经讲过了,如果根据数组构造一颗二叉树。
-* [701.二叉搜索树中的插入操作](https://mp.weixin.qq.com/s/lwKkLQcfbCNX2W-5SOeZEA)
-* [450.删除二叉搜索树中的节点](https://mp.weixin.qq.com/s/-p-Txvch1FFk3ygKLjPAKw)
+* [106.从中序与后序遍历序列构造二叉树](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html)
+* [654.最大二叉树](https://programmercarl.com/0654.最大二叉树.html)中其实已经讲过了,如果根据数组构造一颗二叉树。
+* [701.二叉搜索树中的插入操作](https://programmercarl.com/0701.二叉搜索树中的插入操作.html)
+* [450.删除二叉搜索树中的节点](https://programmercarl.com/0450.删除二叉搜索树中的节点.html)
进入正题:
@@ -38,11 +38,11 @@
其实这里不用强调平衡二叉搜索树,数组构造二叉树,构成平衡树是自然而然的事情,因为大家默认都是从数组中间位置取值作为节点元素,一般不会随机取,**所以想构成不平衡的二叉树是自找麻烦**。
-在[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)和[二叉树:构造一棵最大的二叉树](https://mp.weixin.qq.com/s/1iWJV6Aov23A7xCF4nV88w)中其实已经讲过了,如果根据数组构造一颗二叉树。
+在[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html)和[二叉树:构造一棵最大的二叉树](https://programmercarl.com/0654.最大二叉树.html)中其实已经讲过了,如果根据数组构造一颗二叉树。
**本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间**。
-本题其实要比[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 和 [二叉树:构造一棵最大的二叉树](https://mp.weixin.qq.com/s/1iWJV6Aov23A7xCF4nV88w)简单一些,因为有序数组构造二叉搜索树,寻找分割点就比较容易了。
+本题其实要比[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 和 [二叉树:构造一棵最大的二叉树](https://programmercarl.com/0654.最大二叉树.html)简单一些,因为有序数组构造二叉搜索树,寻找分割点就比较容易了。
分割点就是数组中间位置的节点。
@@ -68,11 +68,11 @@
删除二叉树节点,增加二叉树节点,都是用递归函数的返回值来完成,这样是比较方便的。
-相信大家如果仔细看了[二叉树:搜索树中的插入操作](https://mp.weixin.qq.com/s/lwKkLQcfbCNX2W-5SOeZEA)和[二叉树:搜索树中的删除操作](https://mp.weixin.qq.com/s/-p-Txvch1FFk3ygKLjPAKw),一定会对递归函数返回值的作用深有感触。
+相信大家如果仔细看了[二叉树:搜索树中的插入操作](https://programmercarl.com/0701.二叉搜索树中的插入操作.html)和[二叉树:搜索树中的删除操作](https://programmercarl.com/0450.删除二叉搜索树中的节点.html),一定会对递归函数返回值的作用深有感触。
那么本题要构造二叉树,依然用递归函数的返回值来构造中节点的左右孩子。
-再来看参数,首先是传入数组,然后就是左下表left和右下表right,我们在[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)中提过,在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下表来操作原数组。
+再来看参数,首先是传入数组,然后就是左下表left和右下表right,我们在[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html)中提过,在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下表来操作原数组。
所以代码如下:
@@ -83,7 +83,7 @@ TreeNode* traversal(vector& nums, int left, int right)
这里注意,**我这里定义的是左闭右闭区间,在不断分割的过程中,也会坚持左闭右闭的区间,这又涉及到我们讲过的循环不变量**。
-在[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg),[35.搜索插入位置](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q) 和[59.螺旋矩阵II](https://mp.weixin.qq.com/s/KTPhaeqxbMK9CxHUUgFDmg)都详细讲过循环不变量。
+在[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html),[35.搜索插入位置](https://programmercarl.com/0035.搜索插入位置.html) 和[59.螺旋矩阵II](https://programmercarl.com/0059.螺旋矩阵II.html)都详细讲过循环不变量。
* 确定递归终止条件
@@ -98,7 +98,7 @@ if (left > right) return nullptr;
* 确定单层递归的逻辑
-首先取数组中间元素的位置,不难写出`int mid = (left + right) / 2;`,**这么写其实有一个问题,就是数值越界,例如left和right都是最大int,这么操作就越界了,在[二分法](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)中尤其需要注意!**
+首先取数组中间元素的位置,不难写出`int mid = (left + right) / 2;`,**这么写其实有一个问题,就是数值越界,例如left和right都是最大int,这么操作就越界了,在[二分法](https://programmercarl.com/0035.搜索插入位置.html)中尤其需要注意!**
所以可以这么写:`int mid = left + ((right - left) / 2);`
@@ -122,7 +122,7 @@ return root;
* 递归整体代码如下:
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal(vector& nums, int left, int right) {
@@ -150,7 +150,7 @@ public:
模拟的就是不断分割的过程,C++代码如下:(我已经详细注释)
-```C++
+```CPP
class Solution {
public:
TreeNode* sortedArrayToBST(vector& nums) {
@@ -192,9 +192,9 @@ public:
};
```
-## 总结
+# 总结
-**在[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 和 [二叉树:构造一棵最大的二叉树](https://mp.weixin.qq.com/s/1iWJV6Aov23A7xCF4nV88w)之后,我们顺理成章的应该构造一下二叉搜索树了,一不小心还是一棵平衡二叉搜索树**。
+**在[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 和 [二叉树:构造一棵最大的二叉树](https://programmercarl.com/0654.最大二叉树.html)之后,我们顺理成章的应该构造一下二叉搜索树了,一不小心还是一棵平衡二叉搜索树**。
其实思路也是一样的,不断中间分割,然后递归处理左区间,右区间,也可以说是分治。
@@ -205,10 +205,10 @@ public:
最后依然给出迭代的方法,其实就是模拟取中间元素,然后不断分割去构造二叉树的过程。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
递归: 左闭右开 [left,right)
```Java
@@ -253,7 +253,8 @@ class Solution {
return root;
}
}
-```
+```
+
迭代: 左闭右闭 [left,right]
```java
class Solution {
@@ -303,15 +304,10 @@ class Solution {
}
```
-Python:
+## Python
+
+递归法:
```python3
-# 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 sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def buildaTree(left,right):
@@ -326,21 +322,11 @@ class Solution:
return root
```
-Go:
+## Go
-
-> 递归(隐含回溯)
+递归(隐含回溯)
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
- //递归(隐含回溯)
func sortedArrayToBST(nums []int) *TreeNode {
if len(nums)==0{return nil}//终止条件,最后数组为空则可以返回
root:=&TreeNode{nums[len(nums)/2],nil,nil}//按照BSL的特点,从中间构造节点
@@ -350,21 +336,9 @@ func sortedArrayToBST(nums []int) *TreeNode {
}
```
-JavaScript版本
+## JavaScript
```javascript
-/**
- * Definition for a binary tree node.
- * function TreeNode(val, left, right) {
- * this.val = (val===undefined ? 0 : val)
- * this.left = (left===undefined ? null : left)
- * this.right = (right===undefined ? null : right)
- * }
- */
-/**
- * @param {number[]} nums
- * @return {TreeNode}
- */
var sortedArrayToBST = function (nums) {
const buildTree = (Arr, left, right) => {
if (left > right)
@@ -388,4 +362,4 @@ var sortedArrayToBST = function (nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md
index b6e50853..43a49758 100644
--- a/problems/0110.平衡二叉树.md
+++ b/problems/0110.平衡二叉树.md
@@ -9,9 +9,9 @@
> 求高度还是求深度,你搞懂了不?
-## 110.平衡二叉树
+# 110.平衡二叉树
-题目地址:https://leetcode-cn.com/problems/balanced-binary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/balanced-binary-tree/)
给定一个二叉树,判断它是否是高度平衡的二叉树。
@@ -33,9 +33,10 @@
返回 false 。
-## 题外话
+# 题外话
-咋眼一看这道题目和[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)很像,其实有很大区别。
+
+咋眼一看这道题目和[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)很像,其实有很大区别。
这里强调一波概念:
@@ -50,13 +51,13 @@
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
-有的同学一定疑惑,为什么[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中求的是二叉树的最大深度,也用的是后序遍历。
+有的同学一定疑惑,为什么[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)中求的是二叉树的最大深度,也用的是后序遍历。
**那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这颗树的最大深度,所以才可以使用后序遍历。**
-在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历)
+在[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历)
-```C++
+```CPP
class Solution {
public:
int result;
@@ -90,7 +91,7 @@ public:
注意以上代码是为了把细节体现出来,简化一下代码如下:
-```C++
+```CPP
class Solution {
public:
int result;
@@ -114,9 +115,9 @@ public:
};
```
-## 本题思路
+# 本题思路
-### 递归
+## 递归
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
@@ -160,7 +161,7 @@ if (node == NULL) {
代码如下:
-```
+```CPP
int leftDepth = depth(node->left); // 左
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right); // 右
@@ -178,7 +179,7 @@ return result;
代码精简之后如下:
-```
+```CPP
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
@@ -190,7 +191,7 @@ return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
getDepth整体代码如下:
-```C++
+```CPP
int getDepth(TreeNode* node) {
if (node == NULL) {
return 0;
@@ -205,7 +206,7 @@ int getDepth(TreeNode* node) {
最后本题整体递归代码如下:
-```C++
+```CPP
class Solution {
public:
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
@@ -225,9 +226,9 @@ public:
};
```
-### 迭代
+## 迭代
-在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
+在[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
本题的迭代方式可以先定义一个函数,专门用来求高度。
@@ -235,7 +236,7 @@ public:
代码如下:
-```C++
+```CPP
// cur节点的最大深度,就是cur的高度
int getDepth(TreeNode* cur) {
stack st;
@@ -266,7 +267,7 @@ int getDepth(TreeNode* cur) {
然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
-```
+```CPP
bool isBalanced(TreeNode* root) {
stack st;
if (root == NULL) return true;
@@ -286,7 +287,7 @@ bool isBalanced(TreeNode* root) {
整体代码如下:
-```
+```CPP
class Solution {
private:
int getDepth(TreeNode* cur) {
@@ -342,7 +343,7 @@ public:
因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。
-## 总结
+# 总结
通过本题可以了解求二叉树深度 和 二叉树高度的差异,求深度适合用前序遍历,而求高度适合用后序遍历。
@@ -351,9 +352,9 @@ public:
但是递归方式是一定要掌握的!
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -494,9 +495,9 @@ class Solution {
}
```
-Python:
+## Python
-> 递归法:
+递归法:
```python
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
@@ -513,7 +514,7 @@ class Solution:
return -1 if abs(leftDepth - rightDepth)>1 else 1 + max(leftDepth, rightDepth)
```
-> 迭代法:
+迭代法:
```python
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
@@ -553,7 +554,7 @@ class Solution:
```
-Go:
+## Go
```Go
func isBalanced(root *TreeNode) bool {
if root==nil{
@@ -589,7 +590,7 @@ func abs(a int)int{
}
```
-JavaScript:
+## JavaScript
```javascript
var isBalanced = function(root) {
//还是用递归三部曲 + 后序遍历 左右中 当前左子树右子树高度相差大于1就返回-1
@@ -623,4 +624,4 @@ var isBalanced = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md
index a36faeff..12eadd60 100644
--- a/problems/0111.二叉树的最小深度.md
+++ b/problems/0111.二叉树的最小深度.md
@@ -9,9 +9,9 @@
> 和求最大深度一个套路?
-## 111.二叉树的最小深度
+# 111.二叉树的最小深度
-题目地址:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/)
给定一个二叉树,找出其最小深度。
@@ -27,9 +27,9 @@
返回它的最小深度 2.
-## 思路
+# 思路
-看完了这篇[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),再来看看如何求最小深度。
+看完了这篇[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html),再来看看如何求最小深度。
直觉上好像和求最大深度差不多,其实还是差不少的。
@@ -87,7 +87,7 @@ return result;
代码如下:
-```C++
+```CPP
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
// 中
@@ -106,7 +106,7 @@ return result;
遍历的顺序为后序(左右中),可以看出:**求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。**
整体递归代码如下:
-```C++
+```CPP
class Solution {
public:
int getDepth(TreeNode* node) {
@@ -134,7 +134,7 @@ public:
精简之后代码如下:
-```C++
+```CPP
class Solution {
public:
int minDepth(TreeNode* root) {
@@ -154,15 +154,15 @@ public:
## 迭代法
-相对于[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),本题还可以使用层序遍历的方式来解决,思路是一样的。
+相对于[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html),本题还可以使用层序遍历的方式来解决,思路是一样的。
-如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
+如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
代码如下:(详细注释)
-```C++
+```CPP
class Solution {
public:
@@ -190,10 +190,10 @@ public:
```
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -253,7 +253,7 @@ class Solution {
}
```
-Python:
+## Python
递归法:
@@ -299,7 +299,7 @@ class Solution:
```
-Go:
+## Go
```go
/**
@@ -360,7 +360,7 @@ func minDepth(root *TreeNode) int {
```
-JavaScript:
+## JavaScript
递归法:
@@ -413,4 +413,4 @@ var minDepth = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md
index ae9a9267..da62452c 100644
--- a/problems/0112.路径总和.md
+++ b/problems/0112.路径总和.md
@@ -9,16 +9,16 @@
> 递归函数什么时候需要返回值
-相信很多同学都会疑惑,递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。那么
+相信很多同学都会疑惑,递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。
-接下来我通过详细讲解如下两道题,来回答这个问题:
+那么接下来我通过详细讲解如下两道题,来回答这个问题:
* 112.路径总和
-* 113.路径总和II
+* 113.路径总和ii
-## 112. 路径总和
+# 112. 路径总和
-题目地址:https://leetcode-cn.com/problems/path-sum/
+[力扣题目链接](https://leetcode-cn.com/problems/path-sum/)
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
@@ -31,11 +31,11 @@
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
-### 思路
+# 思路
这道题我们要遍历从根节点到叶子节点的的路径看看总和是不是目标和。
-### 递归
+## 递归
可以使用深度优先遍历的方式(本题前中后序都可以,无所谓,因为中节点也没有处理逻辑)来遍历二叉树
@@ -43,13 +43,11 @@
参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。
-**再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?**
+再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
-在文章[二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)中,我给出了一个结论:
-
-**如果需要搜索整颗二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。**
-
-在[二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)中,因为要遍历树的所有路径,找出深度最深的叶子节点,所以递归函数不要返回值。
+* 如果需要搜索整颗二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
+* 如果需要搜索整颗二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在[236. 二叉树的最近公共祖先](https://programmercarl.com/0236.二叉树的最近公共祖先.html)中介绍)
+* 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回,那么返回类型是什么呢?
@@ -62,7 +60,7 @@
所以代码如下:
```
-bool traversal(TreeNode* cur, int count) // 注意函数的返回类型
+bool traversal(treenode* cur, int count) // 注意函数的返回类型
```
@@ -91,7 +89,7 @@ if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找
代码如下:
-```C++
+```cpp
if (cur->left) { // 左 (空节点不遍历)
// 遇到叶子节点返回true,则直接返回true
if (traversal(cur->left, count - cur->left->val)) return true; // 注意这里有回溯的逻辑
@@ -109,7 +107,7 @@ return false;
为了把回溯的过程体现出来,可以改为如下代码:
-```C++
+```cpp
if (cur->left) { // 左
count -= cur->left->val; // 递归,处理节点;
if (traversal(cur->left, count)) return true;
@@ -126,10 +124,10 @@ return false;
整体代码如下:
-```C++
-class Solution {
+```cpp
+class solution {
private:
- bool traversal(TreeNode* cur, int count) {
+ bool traversal(treenode* cur, int count) {
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回
@@ -147,8 +145,8 @@ private:
}
public:
- bool hasPathSum(TreeNode* root, int sum) {
- if (root == NULL) return false;
+ bool haspathsum(treenode* root, int sum) {
+ if (root == null) return false;
return traversal(root, sum - root->val);
}
};
@@ -156,15 +154,15 @@ public:
以上代码精简之后如下:
-```C++
-class Solution {
+```cpp
+class solution {
public:
- bool hasPathSum(TreeNode* root, int sum) {
- if (root == NULL) return false;
+ bool haspathsum(treenode* root, int sum) {
+ if (root == null) return false;
if (!root->left && !root->right && sum == root->val) {
return true;
}
- return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
+ return haspathsum(root->left, sum - root->val) || haspathsum(root->right, sum - root->val);
}
};
```
@@ -172,43 +170,43 @@ public:
**是不是发现精简之后的代码,已经完全看不出分析的过程了,所以我们要把题目分析清楚之后,在追求代码精简。** 这一点我已经强调很多次了!
-### 迭代
+## 迭代
如果使用栈模拟递归的话,那么如果做回溯呢?
**此时栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和。**
-C++就我们用pair结构来存放这个栈里的元素。
+c++就我们用pair结构来存放这个栈里的元素。
-定义为:`pair` pair<节点指针,路径数值>
+定义为:`pair` pair<节点指针,路径数值>
这个为栈里的一个元素。
如下代码是使用栈模拟的前序遍历,如下:(详细注释)
-```C++
-class Solution {
+```cpp
+class solution {
public:
- bool hasPathSum(TreeNode* root, int sum) {
- if (root == NULL) return false;
+ bool haspathsum(treenode* root, int sum) {
+ if (root == null) return false;
// 此时栈里要放的是pair<节点指针,路径数值>
- stack> st;
- st.push(pair(root, root->val));
+ stack> st;
+ st.push(pair(root, root->val));
while (!st.empty()) {
- pair node = st.top();
+ pair node = st.top();
st.pop();
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
if (!node.first->left && !node.first->right && sum == node.second) return true;
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->right) {
- st.push(pair(node.first->right, node.second + node.first->right->val));
+ st.push(pair(node.first->right, node.second + node.first->right->val));
}
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->left) {
- st.push(pair(node.first->left, node.second + node.first->left->val));
+ st.push(pair(node.first->left, node.second + node.first->left->val));
}
}
return false;
@@ -216,11 +214,11 @@ public:
};
```
-如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和II做了。
+如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和ii做了。
-## 113. 路径总和II
+# 113. 路径总和ii
-题目地址:https://leetcode-cn.com/problems/path-sum-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/path-sum-ii/)
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
@@ -230,26 +228,27 @@ public:
给定如下二叉树,以及目标和 sum = 22,
-
+
-### 思路
+## 思路
-113.路径总和II要遍历整个树,找到所有路径,**所以递归函数不要返回值!**
+
+113.路径总和ii要遍历整个树,找到所有路径,**所以递归函数不要返回值!**
如图:
-
+
为了尽可能的把细节体现出来,我写出如下代码(**这份代码并不简洁,但是逻辑非常清晰**)
-```C++
-class Solution {
+```cpp
+class solution {
private:
vector> result;
vector path;
// 递归函数不需要返回值,因为我们要遍历整个树
- void traversal(TreeNode* cur, int count) {
+ void traversal(treenode* cur, int count) {
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
result.push_back(path);
return;
@@ -275,10 +274,10 @@ private:
}
public:
- vector> pathSum(TreeNode* root, int sum) {
+ vector> pathsum(treenode* root, int sum) {
result.clear();
path.clear();
- if (root == NULL) return result;
+ if (root == null) return result;
path.push_back(root->val); // 把根节点放进路径
traversal(root, sum - root->val);
return result;
@@ -286,11 +285,11 @@ public:
};
```
-至于113. 路径总和II 的迭代法我并没有写,用迭代方式记录所有路径比较麻烦,也没有必要,如果大家感兴趣的话,可以再深入研究研究。
+至于113. 路径总和ii 的迭代法我并没有写,用迭代方式记录所有路径比较麻烦,也没有必要,如果大家感兴趣的话,可以再深入研究研究。
-## 总结
+# 总结
-本篇通过leetcode上112. 路径总和 和 113. 路径总和II 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。
+本篇通过leetcode上112. 路径总和 和 113. 路径总和ii 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。
这两道题目是掌握这一知识点非常好的题目,大家看完本篇文章再去做题,就会感受到搜索整棵树和搜索某一路径的差别。
@@ -299,31 +298,30 @@ public:
+# 其他语言版本
+## java
-## 其他语言版本
-
-
-Java:
-```Java
-class Solution {
- public boolean hasPathSum(TreeNode root, int targetSum) {
+lc112
+```java
+class solution {
+ public boolean haspathsum(treenode root, int targetsum) {
if (root == null) {
return false;
}
- targetSum -= root.val;
+ targetsum -= root.val;
// 叶子结点
if (root.left == null && root.right == null) {
- return targetSum == 0;
+ return targetsum == 0;
}
if (root.left != null) {
- boolean left = hasPathSum(root.left, targetSum);
+ boolean left = haspathsum(root.left, targetsum);
if (left) {// 已经找到
return true;
}
}
if (root.right != null) {
- boolean right = hasPathSum(root.right, targetSum);
+ boolean right = haspathsum(root.right, targetsum);
if (right) {// 已经找到
return true;
}
@@ -332,34 +330,34 @@ class Solution {
}
}
-// LC112 简洁方法
-class Solution {
- public boolean hasPathSum(TreeNode root, int targetSum) {
+// lc112 简洁方法
+class solution {
+ public boolean haspathsum(treenode root, int targetsum) {
if (root == null) return false; // 为空退出
// 叶子节点判断是否符合
- if (root.left == null && root.right == null) return root.val == targetSum;
+ if (root.left == null && root.right == null) return root.val == targetsum;
// 求两侧分支的路径和
- return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
+ return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
}
}
```
迭代
```java
-class Solution {
- public boolean hasPathSum(TreeNode root, int targetSum) {
+class solution {
+ public boolean haspathsum(treenode root, int targetsum) {
if(root==null)return false;
- Stack stack1 = new Stack<>();
- Stack stack2 = new Stack<>();
+ stack stack1 = new stack<>();
+ stack stack2 = new stack<>();
stack1.push(root);stack2.push(root.val);
- while(!stack1.isEmpty()){
+ while(!stack1.isempty()){
int size = stack1.size();
for(int i=0;i> pathSum(TreeNode root, int targetSum) {
- List> res = new ArrayList<>();
+class solution {
+ public list> pathsum(treenode root, int targetsum) {
+ list> res = new arraylist<>();
if (root == null) return res; // 非空判断
- List path = new LinkedList<>();
- preorderDFS(root, targetSum, res, path);
+ list path = new linkedlist<>();
+ preorderdfs(root, targetsum, res, path);
return res;
}
- public void preorderDFS(TreeNode root, int targetSum, List> res, List path) {
+ public void preorderdfs(treenode root, int targetsum, list> res, list path) {
path.add(root.val);
// 遇到了叶子节点
if (root.left == null && root.right == null) {
- // 找到了和为 targetSum 的路径
- if (targetSum - root.val == 0) {
- res.add(new ArrayList<>(path));
+ // 找到了和为 targetsum 的路径
+ if (targetsum - root.val == 0) {
+ res.add(new arraylist<>(path));
}
- return; // 如果和不为 targetSum,返回
+ return; // 如果和不为 targetsum,返回
}
if (root.left != null) {
- preorderDFS(root.left, targetSum - root.val, res, path);
+ preorderdfs(root.left, targetsum - root.val, res, path);
path.remove(path.size() - 1); // 回溯
}
if (root.right != null) {
- preorderDFS(root.right, targetSum - root.val, res, path);
+ preorderdfs(root.right, targetsum - root.val, res, path);
path.remove(path.size() - 1); // 回溯
}
}
}
```
-Python:
+```java
+// 解法2
+class Solution {
+ List> result;
+ LinkedList path;
+ public List> pathSum (TreeNode root,int targetSum) {
+ result = new LinkedList<>();
+ path = new LinkedList<>();
+ travesal(root, targetSum);
+ return result;
+ }
+ private void travesal(TreeNode root, int count) {
+ if (root == null) return;
+ path.offer(root.val);
+ count -= root.val;
+ if (root.left == null && root.right == null && count == 0) {
+ result.add(new LinkedList<>(path));
+ }
+ travesal(root.left, count);
+ travesal(root.right, count);
+ path.removeLast(); // 回溯
+ }
+}
+```
+
+## python
0112.路径总和
+
+**递归**
```python
-# 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 hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
- def isornot(root,targetSum)->bool:
- if (not root.left) and (not root.right) and targetSum == 0:return True // 遇到叶子节点,并且计数为0
- if (not root.left) and (not root.right):return False //遇到叶子节点,计数不为0
+class solution:
+ def haspathsum(self, root: treenode, targetsum: int) -> bool:
+ def isornot(root, targetsum) -> bool:
+ if (not root.left) and (not root.right) and targetsum == 0:
+ return true # 遇到叶子节点,并且计数为0
+ if (not root.left) and (not root.right):
+ return false # 遇到叶子节点,计数不为0
if root.left:
- targetSum -= root.left.val //左节点
- if isornot(root.left,targetSum):return True //递归,处理左节点
- targetSum += root.left.val //回溯
+ targetsum -= root.left.val # 左节点
+ if isornot(root.left, targetsum): return true # 递归,处理左节点
+ targetsum += root.left.val # 回溯
if root.right:
- targetSum -= root.right.val //右节点
- if isornot(root.right,targetSum):return True //递归,处理右节点
- targetSum += root.right.val //回溯
- return False
-
- if root == None:return False //别忘记处理空TreeNode
- else:return isornot(root,targetSum-root.val)
+ targetsum -= root.right.val # 右节点
+ if isornot(root.right, targetsum): return true # 递归,处理右节点
+ targetsum += root.right.val # 回溯
+ return false
+
+ if root == none:
+ return false # 别忘记处理空treenode
+ else:
+ return isornot(root, targetsum - root.val)
+```
+
+**迭代 - 层序遍历**
+```python
+class solution:
+ def haspathsum(self, root: treenode, targetsum: int) -> bool:
+ if not root:
+ return false
+
+ stack = [] # [(当前节点,路径数值), ...]
+ stack.append((root, root.val))
+
+ while stack:
+ cur_node, path_sum = stack.pop()
+
+ if not cur_node.left and not cur_node.right and path_sum == targetsum:
+ return true
+
+ if cur_node.right:
+ stack.append((cur_node.right, path_sum + cur_node.right.val))
+
+ if cur_node.left:
+ stack.append((cur_node.left, path_sum + cur_node.left.val))
+
+ return false
```
0113.路径总和-ii
+
+**递归**
```python
-# 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 pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
- path=[]
- res=[]
- def pathes(root,targetSum):
- if (not root.left) and (not root.right) and targetSum == 0: // 遇到叶子节点,并且计数为0
- res.append(path[:]) //找到一种路径,记录到res中,注意必须是path[:]而不是path
- return
- if (not root.left) and (not root.right):return // 遇到叶子节点直接返回
- if root.left: //左
- targetSum -= root.left.val
- path.append(root.left.val) //递归前记录节点
- pathes(root.left,targetSum) //递归
- targetSum += root.left.val //回溯
- path.pop() //回溯
- if root.right: //右
- targetSum -= root.right.val
- path.append(root.right.val) //递归前记录节点
- pathes(root.right,targetSum) //递归
- targetSum += root.right.val //回溯
- path.pop() //回溯
- return
-
- if root == None:return [] //处理空TreeNode
- else:
- path.append(root.val) //首先处理根节点
- pathes(root,targetSum-root.val)
- return res
+class solution:
+ def pathsum(self, root: treenode, targetsum: int) -> list[list[int]]:
+
+ def traversal(cur_node, remain):
+ if not cur_node.left and not cur_node.right and remain == 0:
+ result.append(path[:])
+ return
+
+ if not cur_node.left and not cur_node.right: return
+
+ if cur_node.left:
+ path.append(cur_node.left.val)
+ remain -= cur_node.left.val
+ traversal(cur_node.left, remain)
+ path.pop()
+ remain += cur_node.left.val
+
+ if cur_node.right:
+ path.append(cur_node.right.val)
+ remain -= cur_node.right.val
+ traversal(cur_node.right, remain)
+ path.pop()
+ remain += cur_node.right.val
+
+ result, path = [], []
+ if not root:
+ return []
+ path.append(root.val)
+ traversal(root, targetsum - root.val)
+ return result
```
-Go:
+## go
-> 112. 路径总和
+112. 路径总和
```go
//递归法
/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
+ * definition for a binary tree node.
+ * type treenode struct {
+ * val int
+ * left *treenode
+ * right *treenode
* }
*/
-func hasPathSum(root *TreeNode, targetSum int) bool {
+func haspathsum(root *treenode, targetsum int) bool {
var flage bool //找没找到的标志
if root==nil{
return flage
}
- pathSum(root,0,targetSum,&flage)
+ pathsum(root,0,targetsum,&flage)
return flage
}
-func pathSum(root *TreeNode, sum int,targetSum int,flage *bool){
- sum+=root.Val
- if root.Left==nil&&root.Right==nil&&sum==targetSum{
+func pathsum(root *treenode, sum int,targetsum int,flage *bool){
+ sum+=root.val
+ if root.left==nil&&root.right==nil&&sum==targetsum{
*flage=true
return
}
- if root.Left!=nil&&!(*flage){//左节点不为空且还没找到
- pathSum(root.Left,sum,targetSum,flage)
+ if root.left!=nil&&!(*flage){//左节点不为空且还没找到
+ pathsum(root.left,sum,targetsum,flage)
}
- if root.Right!=nil&&!(*flage){//右节点不为空且没找到
- pathSum(root.Right,sum,targetSum,flage)
+ if root.right!=nil&&!(*flage){//右节点不为空且没找到
+ pathsum(root.right,sum,targetsum,flage)
}
}
```
-
-
-> 113 递归法
+113 递归法
```go
/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
+ * definition for a binary tree node.
+ * type treenode struct {
+ * val int
+ * left *treenode
+ * right *treenode
* }
*/
-func pathSum(root *TreeNode, targetSum int) [][]int {
+func pathsum(root *treenode, targetsum int) [][]int {
var result [][]int//最终结果
if root==nil{
return result
}
- var sumNodes []int//经过路径的节点集合
- hasPathSum(root,&sumNodes,targetSum,&result)
+ var sumnodes []int//经过路径的节点集合
+ haspathsum(root,&sumnodes,targetsum,&result)
return result
}
-func hasPathSum(root *TreeNode,sumNodes *[]int,targetSum int,result *[][]int){
- *sumNodes=append(*sumNodes,root.Val)
- if root.Left==nil&&root.Right==nil{//叶子节点
- fmt.Println(*sumNodes)
+func haspathsum(root *treenode,sumnodes *[]int,targetsum int,result *[][]int){
+ *sumnodes=append(*sumnodes,root.val)
+ if root.left==nil&&root.right==nil{//叶子节点
+ fmt.println(*sumnodes)
var sum int
var number int
- for k,v:=range *sumNodes{//求该路径节点的和
+ for k,v:=range *sumnodes{//求该路径节点的和
sum+=v
number=k
}
- tempNodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumNodes的值
- for k,v:=range *sumNodes{
- tempNodes[k]=v
+ tempnodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumnodes的值
+ for k,v:=range *sumnodes{
+ tempnodes[k]=v
}
- if sum==targetSum{
- *result=append(*result,tempNodes)
+ if sum==targetsum{
+ *result=append(*result,tempnodes)
}
}
- if root.Left!=nil{
- hasPathSum(root.Left,sumNodes,targetSum,result)
- *sumNodes=(*sumNodes)[:len(*sumNodes)-1]//回溯
+ if root.left!=nil{
+ haspathsum(root.left,sumnodes,targetsum,result)
+ *sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯
}
- if root.Right!=nil{
- hasPathSum(root.Right,sumNodes,targetSum,result)
- *sumNodes=(*sumNodes)[:len(*sumNodes)-1]//回溯
+ if root.right!=nil{
+ haspathsum(root.right,sumnodes,targetsum,result)
+ *sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯
}
}
```
-JavaScript:
+## javascript
0112.路径总和
```javascript
/**
- * @param {TreeNode} root
- * @param {number} targetSum
+ * @param {treenode} root
+ * @param {number} targetsum
* @return {boolean}
*/
-let hasPathSum = function (root, targetSum) {
+let haspathsum = function (root, targetsum) {
// 递归法
const traversal = (node, cnt) => {
// 遇到叶子节点,并且计数为0
@@ -597,19 +637,19 @@ let hasPathSum = function (root, targetSum) {
return false;
};
if (!root) return false;
- return traversal(root, targetSum - root.val);
+ return traversal(root, targetsum - root.val);
// 精简代码:
// if (!root) return false;
- // if (!root.left && !root.right && targetSum === root.val) return true;
- // return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
+ // if (!root.left && !root.right && targetsum === root.val) return true;
+ // return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
};
```
0113.路径总和-ii
```javascript
-let pathSum = function (root, targetSum) {
+let pathsum = function (root, targetsum) {
// 递归法
// 要遍历整个树找到所有路径,所以递归函数不需要返回值, 与112不同
const res = [];
@@ -635,62 +675,29 @@ let pathSum = function (root, targetSum) {
return;
};
if (!root) return res;
- travelsal(root, targetSum - root.val, [root.val]); // 把根节点放进路径
+ travelsal(root, targetsum - root.val, [root.val]); // 把根节点放进路径
return res;
};
```
-
-0112 路径总和
+113 路径总和 精简版
```javascript
-var hasPathSum = function(root, targetSum) {
+var pathsum = function(root, targetsum) {
//递归方法
- // 1. 确定函数参数
- const traversal = function(node,count){
- // 2. 确定终止条件
- if(node.left===null&&node.right===null&&count===0){
- return true;
- }
- if(node.left===null&&node.right===null){
- return false;
- }
- //3. 单层递归逻辑
- if(node.left){
- if(traversal(node.left,count-node.left.val)){
- return true;
- }
- }
- if(node.right){
- if(traversal(node.right,count-node.right.val)){
- return true;
- }
- }
- return false;
- }
- if(root===null){
- return false;
- }
- return traversal(root,targetSum-root.val);
-};
-```
-113 路径总和
-```javascript
-var pathSum = function(root, targetSum) {
- //递归方法
- let resPath = [],curPath = [];
+ let respath = [],curpath = [];
// 1. 确定递归函数参数
- const travelTree = function(node,count){
- curPath.push(node.val);
+ const traveltree = function(node,count){
+ curpath.push(node.val);
count-=node.val;
if(node.left===null&&node.right===null&&count===0){
- resPath.push([...curPath]);
+ respath.push([...curpath]);
}
- node.left&&travelTree(node.left,count);
- node.right&&travelTree(node.right,count);
- let cur = curPath.pop();
+ node.left&&traveltree(node.left,count);
+ node.right&&traveltree(node.right,count);
+ let cur = curpath.pop();
count-=cur;
}
if(root===null){
- return resPath;
+ return respath;
}
travelTree(root,targetSum);
return resPath;
@@ -699,8 +706,9 @@ var pathSum = function(root, targetSum) {
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0115.不同的子序列.md b/problems/0115.不同的子序列.md
index 014eb3cd..908682dd 100644
--- a/problems/0115.不同的子序列.md
+++ b/problems/0115.不同的子序列.md
@@ -8,7 +8,7 @@
## 115.不同的子序列
-题目链接:https://leetcode-cn.com/problems/distinct-subsequences/
+[力扣题目链接](https://leetcode-cn.com/problems/distinct-subsequences/)
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
@@ -29,7 +29,7 @@ s 和 t 由英文字母组成
这道题目相对于72. 编辑距离,简单了不少,因为本题相当于只有删除操作,不用考虑替换增加之类的。
-但相对于刚讲过的[动态规划:392.判断子序列](https://mp.weixin.qq.com/s/2pjT4B4fjfOx5iB6N6xyng)就有难度了,这道题目双指针法可就做不了了,来看看动规五部曲分析如下:
+但相对于刚讲过的[动态规划:392.判断子序列](https://programmercarl.com/0392.判断子序列.html)就有难度了,这道题目双指针法可就做不了了,来看看动规五部曲分析如下:
1. 确定dp数组(dp table)以及下标的含义
@@ -82,7 +82,7 @@ dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串
初始化分析完毕,代码如下:
-```C++
+```CPP
vector> dp(s.size() + 1, vector(t.size() + 1));
for (int i = 0; i <= s.size(); i++) dp[i][0] = 1;
for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和dp数组初始化的时候放在一起,但我为了凸显初始化的逻辑,所以还是加上了。
@@ -97,7 +97,7 @@ for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和
代码如下:
-```C++
+```CPP
for (int i = 1; i <= s.size(); i++) {
for (int j = 1; j <= t.size(); j++) {
if (s[i - 1] == t[j - 1]) {
@@ -120,7 +120,7 @@ for (int i = 1; i <= s.size(); i++) {
动规五部曲分析完毕,代码如下:
-```C++
+```CPP
class Solution {
public:
int numDistinct(string s, string t) {
@@ -221,6 +221,30 @@ class SolutionDP2:
```
Go:
+```go
+func numDistinct(s string, t string) int {
+ dp:= make([][]int,len(s)+1)
+ for i:=0;i {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0116.填充每个节点的下一个右侧节点指针.md b/problems/0116.填充每个节点的下一个右侧节点指针.md
index 28e6f645..e43d79b0 100644
--- a/problems/0116.填充每个节点的下一个右侧节点指针.md
+++ b/problems/0116.填充每个节点的下一个右侧节点指针.md
@@ -9,7 +9,7 @@
# 116. 填充每个节点的下一个右侧节点指针
-链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
+[力扣题目链接](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/)
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
@@ -52,7 +52,7 @@ struct Node {
图中cur节点为元素4,那么搭线的逻辑代码:(**注意注释中操作1和操作2和图中的对应关系**)
-```C++
+```CPP
if (cur->left) cur->left->next = cur->right; // 操作1
if (cur->right) {
if (cur->next) cur->right->next = cur->next->left; // 操作2
@@ -63,7 +63,7 @@ if (cur->right) {
理解到这里,使用前序遍历,那么不难写出如下代码:
-```C++
+```CPP
class Solution {
private:
void traversal(Node* cur) {
@@ -87,13 +87,13 @@ public:
## 迭代(层序遍历)
-本题使用层序遍历是最为直观的,如果对层序遍历不了解,看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)。
+本题使用层序遍历是最为直观的,如果对层序遍历不了解,看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)。
层序遍历本来就是一层一层的去遍历,记录一层的头结点(nodePre),然后让nodePre指向当前遍历的节点就可以了。
代码如下:
-```C++
+```CPP
class Solution {
public:
Node* connect(Node* root) {
@@ -130,26 +130,134 @@ public:
## Java
```java
+// 递归法
+class Solution {
+ public void traversal(Node cur) {
+ if (cur == null) return;
+ if (cur.left != null) cur.left.next = cur.right; // 操作1
+ if (cur.right != null) {
+ if(cur.next != null) cur.right.next = cur.next.left; //操作2
+ else cur.right.next = null;
+ }
+ traversal(cur.left); // 左
+ traversal(cur.right); //右
+ }
+ public Node connect(Node root) {
+ traversal(root);
+ return root;
+ }
+}
+```
+```java
+// 迭代法
+class Solution {
+ public Node connect(Node root) {
+ if (root == null) return root;
+ Queue que = new LinkedList();
+ que.offer(root);
+ Node nodePre = null;
+ Node node = null;
+ while (!que.isEmpty()) {
+ int size = que.size();
+ for (int i=0; i 'Node':
+ def traversal(cur: 'Node') -> 'Node':
+ if not cur: return []
+ if cur.left: cur.left.next = cur.right # 操作1
+ if cur.right:
+ if cur.next:
+ cur.right.next = cur.next.left # 操作2
+ else:
+ cur.right.next = None
+ traversal(cur.left) # 左
+ traversal(cur.right) # 右
+ traversal(root)
+ return root
+```
+```python
+# 迭代法
+class Solution:
+ def connect(self, root: 'Node') -> 'Node':
+ if not root: return
+ res = []
+ queue = [root]
+ while queue:
+ size = len(queue)
+ for i in range(size): # 开始每一层的遍历
+ if i==0:
+ nodePre = queue.pop(0) # 记录一层的头结点
+ node = nodePre
+ else:
+ node = queue.pop(0)
+ nodePre.next = node # 本层前一个节点next指向本节点
+ nodePre = nodePre.next
+ if node.left: queue.append(node.left)
+ if node.right: queue.append(node.right)
+ nodePre.next = None # 本层最后一个节点指向None
+ return root
```
-
## Go
```go
+
```
## JavaScript
```js
+const connect = root => {
+ if (!root) return root;
+ // 根节点入队
+ const Q = [root];
+ while (Q.length) {
+ const len = Q.length;
+ // 遍历这一层的所有节点
+ for (let i = 0; i < len; i++) {
+ // 队头出队
+ const node = Q.shift();
+ // 连接
+ if (i < len - 1) {
+ // 新的队头是node的右边元素
+ node.next = Q[0];
+ }
+ // 队头左节点有值,放入队列
+ node.left && Q.push(node.left);
+ // 队头右节点有值,放入队列
+ node.right && Q.push(node.right);
+ }
+ }
+ return root;
+};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md
index 259fff34..b08e4193 100644
--- a/problems/0121.买卖股票的最佳时机.md
+++ b/problems/0121.买卖股票的最佳时机.md
@@ -8,7 +8,7 @@
## 121. 买卖股票的最佳时机
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
@@ -33,7 +33,7 @@
这道题目最直观的想法,就是暴力,找最优间距了。
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices) {
@@ -59,7 +59,7 @@ public:
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices) {
@@ -139,7 +139,7 @@ dp[5][1]就是最终结果。
以上分析完毕,C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -169,7 +169,7 @@ dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
那么我们只需要记录 当前天的dp状态和前一天的dp状态就可以了,可以使用滚动数组来节省空间,代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -214,6 +214,26 @@ class Solution {
}
}
```
+```java
+// 解法1
+class Solution {
+ public int maxProfit(int[] prices) {
+ if (prices == null || prices.length == 0) return 0;
+ int length = prices.length;
+ // dp[i][0]代表第i天持有股票的最大收益
+ // dp[i][1]代表第i天不持有股票的最大收益
+ int[][] dp = new int[length][2];
+ int result = 0;
+ dp[0][0] = -prices[0];
+ dp[0][1] = 0;
+ for (int i = 1; i < length; i++) {
+ dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
+ dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
+ }
+ return dp[length - 1][1];
+ }
+}
+```
``` java
class Solution { // 动态规划解法
@@ -313,6 +333,26 @@ func max(a,b int)int {
}
```
+JavaScript:
+
+```javascript
+const maxProfit = prices => {
+ const len = prices.length;
+ // 创建dp数组
+ const dp = new Array(len).fill([0, 0]);
+ // dp数组初始化
+ dp[0] = [-prices[0], 0];
+ for (let i = 1; i < len; i++) {
+ // 更新dp[i]
+ dp[i] = [
+ Math.max(dp[i - 1][0], -prices[i]),
+ Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]),
+ ];
+ }
+ return dp[len - 1][1];
+};
+```
+
@@ -320,4 +360,4 @@ func max(a,b int)int {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0122.买卖股票的最佳时机II.md b/problems/0122.买卖股票的最佳时机II.md
index 60d4591f..bd837eea 100644
--- a/problems/0122.买卖股票的最佳时机II.md
+++ b/problems/0122.买卖股票的最佳时机II.md
@@ -9,7 +9,7 @@
## 122.买卖股票的最佳时机II
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
@@ -80,7 +80,7 @@
对应C++代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices) {
@@ -99,7 +99,7 @@ public:
动态规划将在下一个系列详细讲解,本题解先给出我的C++代码(带详细注释),感兴趣的同学可以自己先学习一下。
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices) {
@@ -139,17 +139,11 @@ Java:
// 贪心思路
class Solution {
public int maxProfit(int[] prices) {
- int sum = 0;
- int profit = 0;
- int buy = prices[0];
+ int result = 0;
for (int i = 1; i < prices.length; i++) {
- profit = prices[i] - buy;
- if (profit > 0) {
- sum += profit;
- }
- buy = prices[i];
+ result += Math.max(prices[i] - prices[i - 1], 0);
}
- return sum;
+ return result;
}
}
```
@@ -235,8 +229,23 @@ var maxProfit = function(prices) {
};
```
+C:
+```c
+int maxProfit(int* prices, int pricesSize){
+ int result = 0;
+ int i;
+ //从第二个元素开始遍历数组,与之前的元素进行比较
+ for(i = 1; i < pricesSize; ++i) {
+ //若该元素比前面元素大,则说明有利润。代表买入
+ if(prices[i] > prices[i-1])
+ result+= prices[i]-prices[i-1];
+ }
+ return result;
+}
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md
index 8ed70063..5dfe3f0e 100644
--- a/problems/0122.买卖股票的最佳时机II(动态规划).md
+++ b/problems/0122.买卖股票的最佳时机II(动态规划).md
@@ -8,7 +8,7 @@
## 122.买卖股票的最佳时机II
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
@@ -38,12 +38,12 @@
## 思路
-本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法:买卖股票的最佳时机II](https://mp.weixin.qq.com/s/VsTFA6U96l18Wntjcg3fcg),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。
+本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法:买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。
-本题和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)
+本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)
-**在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)一样一样的**。
+**在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)一样一样的**。
所以我们重点讲一讲递推公式。
@@ -57,10 +57,9 @@
* 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
* 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]
+**注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)唯一不同的地方,就是推导dp[i][0]的时候,第i天买入股票的情况**。
-**注意这里和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)唯一不同的地方,就是推导dp[i][0]的时候,第i天买入股票的情况**。
-
-在[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。
+在[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。
而本题,因为一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润。
@@ -70,11 +69,11 @@
* 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
* 第i天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0]
-**注意这里和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!**
+**注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!**
代码如下:(注意代码中的注释,标记了和121.买卖股票的最佳时机唯一不同的地方)
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices) {
@@ -94,7 +93,7 @@ public:
* 时间复杂度:O(n)
* 空间复杂度:O(n)
-大家可以本题和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)的代码几乎一样,唯一的区别在:
+大家可以本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的代码几乎一样,唯一的区别在:
```
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
@@ -106,7 +105,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
这里我依然给出滚动数组的版本,C++代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -122,7 +121,7 @@ public:
return dp[(len - 1) % 2][1];
}
};
-```
+```
* 时间复杂度:O(n)
* 空间复杂度:O(1)
@@ -231,4 +230,4 @@ const maxProfit = (prices) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md
index 7ff1bfe2..6a849c80 100644
--- a/problems/0123.买卖股票的最佳时机III.md
+++ b/problems/0123.买卖股票的最佳时机III.md
@@ -8,7 +8,7 @@
## 123.买卖股票的最佳时机III
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/)
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
@@ -44,7 +44,7 @@
## 思路
-这道题目相对 [121.买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ) 和 [122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w) 难了不少。
+这道题目相对 [121.买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html) 和 [122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html) 难了不少。
关键在于至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。
@@ -127,7 +127,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
以上五部都分析完了,不难写出如下代码:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -153,7 +153,7 @@ public:
当然,大家可以看到力扣官方题解里的一种优化空间写法,我这里给出对应的C++版本:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -278,7 +278,44 @@ class Solution:
return dp[4]
```
-Go:
+JavaScript:
+
+> 版本一:
+
+```javascript
+const maxProfit = prices => {
+ const len = prices.length;
+ const dp = new Array(len).fill(0).map(x => new Array(5).fill(0));
+ dp[0][1] = -prices[0];
+ dp[0][3] = -prices[0];
+ for (let i = 1; i < len; i++) {
+ dp[i][0] = dp[i - 1][0];
+ dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
+ dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
+ dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
+ dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
+ }
+ return dp[len - 1][4];
+};
+```
+
+> 版本二:
+
+```javascript
+const maxProfit = prices => {
+ const len = prices.length;
+ const dp = new Array(5).fill(0);
+ dp[1] = -prices[0];
+ dp[3] = -prices[0];
+ for (let i = 1; i < len; i++) {
+ dp[1] = Math.max(dp[1], dp[0] - prices[i]);
+ dp[2] = Math.max(dp[2], dp[1] + prices[i]);
+ dp[3] = Math.max(dp[3], dp[2] - prices[i]);
+ dp[4] = Math.max(dp[4], dp[3] + prices[i]);
+ }
+ return dp[4];
+};
+```
@@ -287,4 +324,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0127.单词接龙.md b/problems/0127.单词接龙.md
new file mode 100644
index 00000000..e38453ef
--- /dev/null
+++ b/problems/0127.单词接龙.md
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
+# 127. 单词接龙
+
+[力扣题目链接](https://leetcode-cn.com/problems/word-ladder/)
+
+
+字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:
+* 序列中第一个单词是 beginWord 。
+* 序列中最后一个单词是 endWord 。
+* 每次转换只能改变一个字母。
+* 转换过程中的中间单词必须是字典 wordList 中的单词。
+* 给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。
+
+
+示例 1:
+
+* 输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
+* 输出:5
+* 解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
+
+示例 2:
+* 输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
+* 输出:0
+* 解释:endWord "cog" 不在字典中,所以无法进行转换。
+
+
+# 思路
+
+以示例1为例,从这个图中可以看出 hit 到 cog的路线,不止一条,有三条,两条是最短的长度为5,一条长度为6。
+
+
+
+本题只需要求出最短长度就可以了,不用找出路径。
+
+所以这道题要解决两个问题:
+
+* 图中的线是如何连在一起的
+* 起点和终点的最短路径长度
+
+
+首先题目中并没有给出点与点之间的连线,而是要我们自己去连,条件是字符只能差一个,所以判断点与点之间的关系,要自己判断是不是差一个字符,如果差一个字符,那就是有链接。
+
+然后就是求起点和终点的最短路径长度,**这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径**。因为广搜就是以起点中心向四周扩散的搜索。
+
+本题如果用深搜,会非常麻烦。
+
+另外需要有一个注意点:
+
+* 本题是一个无向图,需要用标记位,标记着节点是否走过,否则就会死循环!
+* 本题给出集合是数组型的,可以转成set结构,查找更快一些
+
+C++代码如下:(详细注释)
+
+```CPP
+class Solution {
+public:
+ int ladderLength(string beginWord, string endWord, vector& wordList) {
+ // 将vector转成unordered_set,提高查询速度
+ unordered_set wordSet(wordList.begin(), wordList.end());
+ // 如果endWord没有在wordSet出现,直接返回0
+ if (wordSet.find(endWord) == wordSet.end()) return 0;
+ // 记录word是否访问过
+ unordered_map visitMap; //
+ // 初始化队列
+ queue que;
+ que.push(beginWord);
+ // 初始化visitMap
+ visitMap.insert(pair(beginWord, 1));
+
+ while(!que.empty()) {
+ string word = que.front();
+ que.pop();
+ int path = visitMap[word]; // 这个word的路径长度
+ for (int i = 0; i < word.size(); i++) {
+ string newWord = word; // 用一个新单词替换word,因为每次置换一个字母
+ for (int j = 0 ; j < 26; j++) {
+ newWord[i] = j + 'a';
+ if (newWord == endWord) return path + 1; // 找到了end,返回path+1
+ // wordSet出现了newWord,并且newWord没有被访问过
+ if (wordSet.find(newWord) != wordSet.end()
+ && visitMap.find(newWord) == visitMap.end()) {
+ // 添加访问信息
+ visitMap.insert(pair(newWord, path + 1));
+ que.push(newWord);
+ }
+ }
+ }
+ }
+ return 0;
+ }
+};
+```
+
+# 其他语言版本
+
+## Java
+
+```java
+public int ladderLength(String beginWord, String endWord, List wordList) {
+ HashSet wordSet = new HashSet<>(wordList); //转换为hashset 加快速度
+ if (wordSet.size() == 0 || !wordSet.contains(endWord)) { //特殊情况判断
+ return 0;
+ }
+ Queue queue = new LinkedList<>(); //bfs 队列
+ queue.offer(beginWord);
+ Map map = new HashMap<>(); //记录单词对应路径长度
+ map.put(beginWord, 1);
+
+ while (!queue.isEmpty()) {
+ String word = queue.poll(); //取出队头单词
+ int path = map.get(word); //获取到该单词的路径长度
+ for (int i = 0; i < word.length(); i++) { //遍历单词的每个字符
+ char[] chars = word.toCharArray(); //将单词转换为char array,方便替换
+ for (char k = 'a'; k <= 'z'; k++) { //从'a' 到 'z' 遍历替换
+ chars[i] = k; //替换第i个字符
+ String newWord = String.valueOf(chars); //得到新的字符串
+ if (newWord.equals(endWord)) { //如果新的字符串值与endWord一致,返回当前长度+1
+ return path + 1;
+ }
+ if (wordSet.contains(newWord) && !map.containsKey(newWord)) { //如果新单词在set中,但是没有访问过
+ map.put(newWord, path + 1); //记录单词对应的路径长度
+ queue.offer(newWord);//加入队尾
+ }
+ }
+ }
+ }
+ return 0; //未找到
+}
+```
+
+## Python
+
+## Go
+
+## JavaScript
+
+
+-----------------------
+* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
+* B站视频:[代码随想录](https://space.bilibili.com/525438321)
+* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
+
diff --git a/problems/0129.求根到叶子节点数字之和.md b/problems/0129.求根到叶子节点数字之和.md
index f8c93382..7f50c885 100644
--- a/problems/0129.求根到叶子节点数字之和.md
+++ b/problems/0129.求根到叶子节点数字之和.md
@@ -1,13 +1,13 @@
## 链接
-https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/
+[力扣题目链接](https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/)
## 思路
-本题和[113.路径总和II](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg)是类似的思路,做完这道题,可以顺便把[113.路径总和II](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg) 和 [112.路径总和](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg) 做了。
+本题和[113.路径总和II](https://programmercarl.com/0112.路径总和.html#_113-路径总和ii)是类似的思路,做完这道题,可以顺便把[113.路径总和II](https://programmercarl.com/0112.路径总和.html#_113-路径总和ii) 和 [112.路径总和](https://programmercarl.com/0112.路径总和.html#_112-路径总和) 做了。
-结合112.路径总和 和 113.路径总和II,我在讲了[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg),如果大家对二叉树递归函数什么时候需要返回值很迷茫,可以看一下。
+结合112.路径总和 和 113.路径总和II,我在讲了[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://programmercarl.com/0112.路径总和.html),如果大家对二叉树递归函数什么时候需要返回值很迷茫,可以看一下。
-接下来在看本题,就简单多了,本题其实需要使用回溯,但一些同学可能都不知道自己用了回溯,在[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA)中,我详细讲解了二叉树的递归中,如何使用了回溯。
+接下来在看本题,就简单多了,本题其实需要使用回溯,但一些同学可能都不知道自己用了回溯,在[二叉树:以为使用了递归,其实还隐藏着回溯](https://programmercarl.com/二叉树中递归带着回溯.html)中,我详细讲解了二叉树的递归中,如何使用了回溯。
接下来我们来看题:
@@ -17,11 +17,11 @@ https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/
### 递归三部曲
-如果对递归三部曲不了解的话,可以看这里:[二叉树:前中后递归详解](https://mp.weixin.qq.com/s/PwVIfxDlT3kRgMASWAMGhA)
+如果对递归三部曲不了解的话,可以看这里:[二叉树:前中后递归详解](https://programmercarl.com/二叉树的递归遍历.html)
* 确定递归函数返回值及其参数
-这里我们要遍历整个二叉树,且需要要返回值做逻辑处理,所有返回值为void,在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg)中,详细讲解了返回值问题。
+这里我们要遍历整个二叉树,且需要要返回值做逻辑处理,所有返回值为void,在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://programmercarl.com/0112.路径总和.html)中,详细讲解了返回值问题。
参数只需要把根节点传入,此时还需要定义两个全局遍历,一个是result,记录最终结果,一个是vector path。
@@ -52,7 +52,7 @@ if (!cur->left && !cur->right) { // 遇到了叶子节点
这里vectorToInt函数就是把数组转成int,代码如下:
-```C++
+```CPP
int vectorToInt(const vector& vec) {
int sum = 0;
for (int i = 0; i < vec.size(); i++) {
@@ -78,7 +78,7 @@ int vectorToInt(const vector& vec) {
代码如下:
-```C++
+```CPP
// 中
if (cur->left) { // 左 (空节点不遍历)
path.push_back(cur->left->val);
@@ -94,7 +94,7 @@ if (cur->right) { // 右 (空节点不遍历)
这里要注意回溯和递归要永远在一起,一个递归,对应一个回溯,是一对一的关系,有的同学写成如下代码:
-```C++
+```CPP
if (cur->left) { // 左 (空节点不遍历)
path.push_back(cur->left->val);
traversal(cur->left); // 递归
@@ -111,7 +111,7 @@ path.pop_back(); // 回溯
关键逻辑分析完了,整体C++代码如下:
-```C++
+```CPP
class Solution {
private:
int result;
@@ -164,8 +164,79 @@ public:
Java:
-Python:
+```java
+class Solution {
+ List path = new ArrayList<>();
+ int res = 0;
+ public int sumNumbers(TreeNode root) {
+ // 如果节点为0,那么就返回0
+ if (root == null) return 0;
+ // 首先将根节点放到集合中
+ path.add(root.val);
+ // 开始递归
+ recur(root);
+ return res;
+ }
+ public void recur(TreeNode root){
+ if (root.left == null && root.right == null) {
+ // 当是叶子节点的时候,开始处理
+ res += listToInt(path);
+ return;
+ }
+
+ if (root.left != null){
+ // 注意有回溯
+ path.add(root.left.val);
+ recur(root.left);
+ path.remove(path.size() - 1);
+ }
+ if (root.right != null){
+ // 注意有回溯
+ path.add(root.right.val);
+ recur(root.right);
+ path.remove(path.size() - 1);
+ }
+ return;
+ }
+ public int listToInt(List path){
+ int sum = 0;
+ for (Integer num:path){
+ // sum * 10 表示进位
+ sum = sum * 10 + num;
+ }
+ return sum;
+ }
+}
+```
+
+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:
@@ -176,4 +247,4 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md
index 43453409..f2e108e8 100644
--- a/problems/0131.分割回文串.md
+++ b/problems/0131.分割回文串.md
@@ -11,7 +11,7 @@
## 131.分割回文串
-题目链接:https://leetcode-cn.com/problems/palindrome-partitioning/
+[力扣题目链接](https://leetcode-cn.com/problems/palindrome-partitioning/)
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
@@ -66,11 +66,11 @@
本题递归函数参数还需要startIndex,因为切割过的地方,不能重复切割,和组合问题也是保持一致的。
-在[回溯算法:求组合总和(二)](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)中我们深入探讨了组合问题什么时候需要startIndex,什么时候不需要startIndex。
+在[回溯算法:求组合总和(二)](https://programmercarl.com/0039.组合总和.html)中我们深入探讨了组合问题什么时候需要startIndex,什么时候不需要startIndex。
代码如下:
-```C++
+```CPP
vector> result;
vector path; // 放已经回文的子串
void backtracking (const string& s, int startIndex) {
@@ -88,7 +88,7 @@ void backtracking (const string& s, int startIndex) {
所以终止条件代码如下:
-```C++
+```CPP
void backtracking (const string& s, int startIndex) {
// 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
if (startIndex >= s.size()) {
@@ -108,7 +108,7 @@ void backtracking (const string& s, int startIndex) {
代码如下:
-```C++
+```CPP
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome(s, startIndex, i)) { // 是回文子串
// 获取[startIndex,i]在s中的子串
@@ -132,7 +132,7 @@ for (int i = startIndex; i < s.size(); i++) {
那么判断回文的C++代码如下:
-```C++
+```CPP
bool isPalindrome(const string& s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
if (s[i] != s[j]) {
@@ -143,7 +143,7 @@ for (int i = startIndex; i < s.size(); i++) {
}
```
-如果大家对双指针法有生疏了,传送门:[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
+如果大家对双指针法有生疏了,传送门:[双指针法:总结篇!](https://programmercarl.com/双指针总结.html)
此时关键代码已经讲解完毕,整体代码如下(详细注释了)
@@ -151,7 +151,7 @@ for (int i = startIndex; i < s.size(); i++) {
根据Carl给出的回溯算法模板:
-```C++
+```CPP
void backtracking(参数) {
if (终止条件) {
存放结果;
@@ -169,7 +169,7 @@ void backtracking(参数) {
不难写出如下代码:
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -292,7 +292,8 @@ class Solution {
```
Python:
-```py
+```python
+# 版本一
class Solution:
def partition(self, s: str) -> List[List[str]]:
res = []
@@ -310,7 +311,36 @@ class Solution:
return res
```
-
+```python
+# 版本二
+class Solution:
+ def partition(self, s: str) -> List[List[str]]:
+ res = []
+ path = [] #放已经回文的子串
+ # 双指针法判断是否是回文串
+ def isPalindrome(s):
+ n = len(s)
+ i, j = 0, n - 1
+ while i < j:
+ if s[i] != s[j]:return False
+ i += 1
+ j -= 1
+ return True
+
+ def backtrack(s, startIndex):
+ if startIndex >= len(s): # 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
+ res.append(path[:])
+ return
+ for i in range(startIndex, len(s)):
+ p = s[startIndex:i+1] # 获取[startIndex,i+1]在s中的子串
+ if isPalindrome(p): # 是回文子串
+ path.append(p)
+ else: continue #不是回文,跳过
+ backtrack(s, i + 1)
+ path.pop() #回溯过程,弹出本次已经填在path的子串
+ backtrack(s, 0)
+ return res
+```
Go:
> 注意切片(go切片是披着值类型外衣的引用类型)
@@ -395,4 +425,4 @@ var partition = function(s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0132.分割回文串II.md b/problems/0132.分割回文串II.md
index 856262d6..dbd830d0 100644
--- a/problems/0132.分割回文串II.md
+++ b/problems/0132.分割回文串II.md
@@ -10,7 +10,7 @@
# 132. 分割回文串 II
-链接:https://leetcode-cn.com/problems/palindrome-partitioning-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/palindrome-partitioning-ii/)
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
@@ -29,7 +29,7 @@
示例 3:
输入:s = "ab"
输出:1
-
+
提示:
@@ -38,7 +38,7 @@
# 思路
-我们在讲解回溯法系列的时候,讲过了这道题目[回溯算法:131.分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)。
+我们在讲解回溯法系列的时候,讲过了这道题目[回溯算法:131.分割回文串](https://programmercarl.com/0131.分割回文串.html)。
本题呢其实也可以使用回溯法,只不过会超时!(通过记忆化回溯,也可以过,感兴趣的同学可以自行研究一下)
@@ -46,7 +46,7 @@
关于回文子串,两道题目题目大家是一定要掌握的。
-* [动态规划:647. 回文子串](https://mp.weixin.qq.com/s/2WetyP6IYQ6VotegepVpEw)
+* [动态规划:647. 回文子串](https://programmercarl.com/0647.回文子串.html)
* 5.最长回文子串 和 647.回文子串基本一样的
这两道题目是回文子串的基础题目,本题也要用到相关的知识点。
@@ -101,7 +101,7 @@ dp[i]: 范围是[0, i]的回文子串,最少分割次数是dp[i]。
代码如下:
-```C++
+```CPP
vector dp(s.size(), INT_MAX);
dp[0] = 0;
```
@@ -109,7 +109,7 @@ dp[0] = 0;
其实也可以这样初始化,更具dp[i]的定义,dp[i]的最大值其实就是i,也就是把每个字符分割出来。
所以初始化代码也可以为:
-```C++
+```CPP
vector dp(s.size());
for (int i = 0; i < s.size(); i++) dp[i] = i;
```
@@ -122,7 +122,7 @@ j是在[0,i]之间,所以遍历i的for循环一定在外层,这里遍历j
代码如下:
-```C++
+```CPP
for (int i = 1; i < s.size(); i++) {
if (isPalindromic[0][i]) { // 判断是不是回文子串
dp[i] = 0;
@@ -149,7 +149,7 @@ for (int i = 1; i < s.size(); i++) {
代码如下:
-```C++
+```CPP
vector> isPalindromic(s.size(), vector(s.size(), false));
for (int i = s.size() - 1; i >= 0; i--) {
for (int j = i; j < s.size(); j++) {
@@ -168,7 +168,7 @@ for (int i = s.size() - 1; i >= 0; i--) {
以上分析完毕,代码如下:
-```C++
+```CPP
class Solution {
public:
int minCut(string s) {
@@ -252,5 +252,5 @@ class Solution:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md
index 9b660ea0..526efb14 100644
--- a/problems/0134.加油站.md
+++ b/problems/0134.加油站.md
@@ -9,7 +9,7 @@
## 134. 加油站
-题目链接:https://leetcode-cn.com/problems/gas-station/
+[力扣题目链接](https://leetcode-cn.com/problems/gas-station/)
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
@@ -65,7 +65,7 @@ cost = [3,4,3]
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int canCompleteCircuit(vector& gas, vector& cost) {
@@ -99,7 +99,7 @@ C++暴力解法在leetcode上提交也可以过。
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int canCompleteCircuit(vector& gas, vector& cost) {
@@ -160,7 +160,7 @@ i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int canCompleteCircuit(vector& gas, vector& cost) {
@@ -200,6 +200,7 @@ public:
Java:
```java
+// 解法1
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int sum = 0;
@@ -221,7 +222,26 @@ class Solution {
}
}
```
-
+```java
+// 解法2
+class Solution {
+ public int canCompleteCircuit(int[] gas, int[] cost) {
+ int curSum = 0;
+ int totalSum = 0;
+ int index = 0;
+ for (int i = 0; i < gas.length; i++) {
+ curSum += gas[i] - cost[i];
+ totalSum += gas[i] - cost[i];
+ if (curSum < 0) {
+ index = (i + 1) % gas.length ;
+ curSum = 0;
+ }
+ }
+ if (totalSum < 0) return -1;
+ return index;
+ }
+}
+```
Python:
```python
class Solution:
@@ -261,6 +281,48 @@ func canCompleteCircuit(gas []int, cost []int) int {
```
Javascript:
+暴力:
+```js
+var canCompleteCircuit = function(gas, cost) {
+ for(let i = 0; i < cost.length; i++) {
+ let rest = gas[i] - cost[i] //记录剩余油量
+ // 以i为起点行驶一圈,index为下一个目的地
+ let index = (i + 1) % cost.length
+ while(rest > 0 && index !== i) {
+ rest += gas[index] - cost[index]
+ index = (index + 1) % cost.length
+ }
+ if(rest >= 0 && index === i) return i
+ }
+ return -1
+};
+```
+解法一:
+```js
+var canCompleteCircuit = function(gas, cost) {
+ let curSum = 0
+ let min = Infinity
+ for(let i = 0; i < gas.length; i++) {
+ let rest = gas[i] - cost[i]
+ curSum += rest
+ if(curSum < min) {
+ min = curSum
+ }
+ }
+ if(curSum < 0) return -1 //1.总油量 小于 总消耗量
+ if(min >= 0) return 0 //2. 说明油箱里油没断过
+ //3. 从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点
+ for(let i = gas.length -1; i >= 0; i--) {
+ let rest = gas[i] - cost[i]
+ min += rest
+ if(min >= 0) {
+ return i
+ }
+ }
+ return -1
+}
+```
+解法二:
```Javascript
var canCompleteCircuit = function(gas, cost) {
const gasLen = gas.length
@@ -283,9 +345,38 @@ var canCompleteCircuit = function(gas, cost) {
};
```
+C:
+```c
+int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
+ int curSum = 0;
+ int i;
+ int min = INT_MAX;
+ //遍历整个数组。计算出每站的用油差。并将其与最小累加量比较
+ for(i = 0; i < gasSize; i++) {
+ int diff = gas[i] - cost[i];
+ curSum += diff;
+ if(curSum < min)
+ min = curSum;
+ }
+ //若汽油总数为负数,代表无法跑完一环。返回-1
+ if(curSum < 0)
+ return -1;
+ //若min大于等于0,说明每一天加油量比用油量多。因此从0出发即可
+ if(min >= 0)
+ return 0;
+ //若累加最小值为负,则找到一个非零元素(加油量大于出油量)出发。返回坐标
+ for(i = gasSize - 1; i >= 0; i--) {
+ min+=(gas[i]-cost[i]);
+ if(min >= 0)
+ return i;
+ }
+ //逻辑上不会返回这个0
+ return 0;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md
index 2d3fca84..526b5870 100644
--- a/problems/0135.分发糖果.md
+++ b/problems/0135.分发糖果.md
@@ -9,7 +9,7 @@
## 135. 分发糖果
-链接:https://leetcode-cn.com/problems/candy/
+[力扣题目链接](https://leetcode-cn.com/problems/candy/)
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
@@ -47,7 +47,7 @@
代码如下:
-```C++
+```CPP
// 从前向后
for (int i = 1; i < ratings.size(); i++) {
if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
@@ -80,7 +80,7 @@ for (int i = 1; i < ratings.size(); i++) {
所以该过程代码如下:
-```C++
+```CPP
// 从后向前
for (int i = ratings.size() - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1] ) {
@@ -90,7 +90,7 @@ for (int i = ratings.size() - 2; i >= 0; i--) {
```
整体代码如下:
-```C++
+```CPP
class Solution {
public:
int candy(vector& ratings) {
@@ -242,4 +242,4 @@ var candy = function(ratings) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md
index 4ba08aa9..969d2ce7 100644
--- a/problems/0139.单词拆分.md
+++ b/problems/0139.单词拆分.md
@@ -10,7 +10,7 @@
## 139.单词拆分
-题目链接:https://leetcode-cn.com/problems/word-break/
+[力扣题目链接](https://leetcode-cn.com/problems/word-break/)
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
@@ -37,15 +37,15 @@
## 思路
-看到这道题目的时候,大家应该回想起我们之前讲解回溯法专题的时候,讲过的一道题目[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q),就是枚举字符串的所有分割情况。
+看到这道题目的时候,大家应该回想起我们之前讲解回溯法专题的时候,讲过的一道题目[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html),就是枚举字符串的所有分割情况。
-[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q):是枚举分割后的所有子串,判断是否回文。
+[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html):是枚举分割后的所有子串,判断是否回文。
本道是枚举分割所有字符串,判断是否在字典里出现过。
那么这里我也给出回溯法C++代码:
-```C++
+```CPP
class Solution {
private:
bool backtracking (const string& s, const unordered_set& wordSet, int startIndex) {
@@ -86,7 +86,7 @@ public:
C++代码如下:
-```C++
+```CPP
class Solution {
private:
bool backtracking (const string& s,
@@ -161,11 +161,11 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。
**如果求排列数就是外层for遍历背包,内层for循环遍历物品**。
-对这个结论还有疑问的同学可以看这篇[本周小结!(动态规划系列五)](https://mp.weixin.qq.com/s/znj-9j8mWymRFaPjJN2Qnw),这篇本周小节中,我做了如下总结:
+对这个结论还有疑问的同学可以看这篇[本周小结!(动态规划系列五)](https://programmercarl.com/%E5%91%A8%E6%80%BB%E7%BB%93/20210204动规周末总结.html),这篇本周小节中,我做了如下总结:
-求组合数:[动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)
-求排列数:[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)、[动态规划:70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)
-求最小数:[动态规划:322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)、[动态规划:279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)
+求组合数:[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)
+求排列数:[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和.html)、[动态规划:70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html)
+求最小数:[动态规划:322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)、[动态规划:279.完全平方数](https://programmercarl.com/0279.完全平方数.html)
本题最终要求的是是否都出现过,所以对出现单词集合里的元素是组合还是排列,并不在意!
@@ -190,7 +190,7 @@ dp[s.size()]就是最终结果。
动规五部曲分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool wordBreak(string s, vector& wordDict) {
@@ -215,7 +215,7 @@ public:
## 总结
-本题和我们之前讲解回溯专题的[回溯算法:分割回文串](https://mp.weixin.qq.com/s/Pb1epUTbU8fHIht-g_MS5Q)非常像,所以我也给出了对应的回溯解法。
+本题和我们之前讲解回溯专题的[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)非常像,所以我也给出了对应的回溯解法。
稍加分析,便可知道本题是完全背包,而且是求能否组成背包,所以遍历顺序理论上来讲 两层for循环谁先谁后都可以!
@@ -319,4 +319,4 @@ const wordBreak = (s, wordDict) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0141.环形链表.md b/problems/0141.环形链表.md
index 0871202d..34b8d25f 100644
--- a/problems/0141.环形链表.md
+++ b/problems/0141.环形链表.md
@@ -45,7 +45,7 @@ fast和slow各自再走一步, fast和slow就相遇了
C++代码如下
-```C++
+```CPP
class Solution {
public:
bool hasCycle(ListNode *head) {
@@ -66,7 +66,7 @@ public:
做完这道题目,可以在做做142.环形链表II,不仅仅要找环,还要找环的入口。
-142.环形链表II题解:[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ)
+142.环形链表II题解:[链表:环找到了,那入口呢?](https://programmercarl.com/0142.环形链表II.html)
# 其他语言版本
@@ -120,5 +120,5 @@ class Solution:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md
index 9deb1e0c..bfa779a2 100644
--- a/problems/0142.环形链表II.md
+++ b/problems/0142.环形链表II.md
@@ -13,7 +13,7 @@
## 142.环形链表II
-https://leetcode-cn.com/problems/linked-list-cycle-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/linked-list-cycle-ii/)
题意:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
@@ -109,7 +109,7 @@ fast指针走过的节点数:` x + y + n (y + z)`,n为fast指针在环内走
代码如下:
-```C++
+```CPP
/**
* Definition for singly-linked list.
* struct ListNode {
@@ -146,7 +146,7 @@ public:
在推理过程中,大家可能有一个疑问就是:**为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?**
-即文章[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)中如下的地方:
+即文章[链表:环找到了,那入口呢?](https://programmercarl.com/0142.环形链表II.html)中如下的地方:

@@ -175,7 +175,7 @@ public:
那有同学又说了,为什么fast不能跳过去呢? 在刚刚已经说过一次了,**fast相对于slow是一次移动一个节点,所以不可能跳过去**。
-好了,这次把为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y ,用数学推理了一下,算是对[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)的补充。
+好了,这次把为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y ,用数学推理了一下,算是对[链表:环找到了,那入口呢?](https://programmercarl.com/0142.环形链表II.html)的补充。
## 总结
@@ -296,9 +296,42 @@ var detectCycle = function(head) {
};
```
+Swift:
+```swift
+class Solution {
+ func detectCycle(_ head: ListNode?) -> ListNode? {
+ var slow: ListNode? = head
+ var fast: ListNode? = head
+ while fast != nil && fast?.next != nil {
+ slow = slow?.next
+ fast = fast?.next?.next
+ if slow == fast {
+ // 环内相遇
+ var list1: ListNode? = slow
+ var list2: ListNode? = head
+ while list1 != list2 {
+ list1 = list1?.next
+ list2 = list2?.next
+ }
+ return list2
+ }
+ }
+ return nil
+ }
+}
+extension ListNode: Equatable {
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(val)
+ hasher.combine(ObjectIdentifier(self))
+ }
+ public static func == (lhs: ListNode, rhs: ListNode) -> Bool {
+ return lhs === rhs
+ }
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0143.重排链表.md b/problems/0143.重排链表.md
index c4e8d8f7..2b4e68b7 100644
--- a/problems/0143.重排链表.md
+++ b/problems/0143.重排链表.md
@@ -1,4 +1,3 @@
-
@@ -25,7 +24,7 @@
代码如下:
-```C++
+```CPP
class Solution {
public:
void reorderList(ListNode* head) {
@@ -63,7 +62,8 @@ public:
## 方法二
把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了
-```C++
+
+```CPP
class Solution {
public:
void reorderList(ListNode* head) {
@@ -108,7 +108,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
private:
// 反转链表
@@ -176,15 +176,187 @@ public:
Java:
-Python:
+```java
+// 方法三
+public class ReorderList {
+ public void reorderList(ListNode head) {
+ ListNode fast = head, slow = head;
+ //求出中点
+ while (fast.next != null && fast.next.next != null) {
+ slow = slow.next;
+ fast = fast.next.next;
+ }
+ //right就是右半部分 12345 就是45 1234 就是34
+ ListNode right = slow.next;
+ //断开左部分和右部分
+ slow.next = null;
+ //反转右部分 right就是反转后右部分的起点
+ right = reverseList(right);
+ //左部分的起点
+ ListNode left = head;
+ //进行左右部分来回连接
+ //这里左部分的节点个数一定大于等于右部分的节点个数 因此只判断right即可
+ while (right != null) {
+ ListNode curLeft = left.next;
+ left.next = right;
+ left = curLeft;
+ ListNode curRight = right.next;
+ right.next = left;
+ right = curRight;
+ }
+ }
+
+ public ListNode reverseList(ListNode head) {
+ ListNode headNode = new ListNode(0);
+ ListNode cur = head;
+ ListNode next = null;
+ while (cur != null) {
+ next = cur.next;
+ cur.next = headNode.next;
+ headNode.next = cur;
+ cur = next;
+ }
+ return headNode.next;
+ }
+}
+
+-------------------------------------------------------------------------
+// 方法一 Java实现,使用数组存储节点
+ class Solution {
+ public void reorderList(ListNode head) {
+ // 双指针的做法
+ ListNode cur = head;
+ // ArrayList底层是数组,可以使用下标随机访问
+ List list = new ArrayList<>();
+ while (cur != null){
+ list.add(cur);
+ cur = cur.next;
+ }
+ cur = head; // 重新回到头部
+ int l = 1, r = list.size() - 1; // 注意左边是从1开始
+ int count = 0;
+ while (l <= r){
+ if (count % 2 == 0){
+ // 偶数
+ cur.next = list.get(r);
+ r--;
+ }else {
+ // 奇数
+ cur.next = list.get(l);
+ l++;
+ }
+ // 每一次指针都需要移动
+ cur = cur.next;
+ count++;
+ }
+ // 当是偶数的话,需要做额外处理
+ if (list.size() % 2== 0){
+ cur.next = list.get(l);
+ cur = cur.next;
+ }
+
+ // 注意结尾要结束一波
+ cur.next = null;
+ }
+}
+-------------------------------------------------------------------------
+// 方法二:使用双端队列,简化了数组的操作,代码相对于前者更简洁(避免一些边界条件)
+class Solution {
+ public void reorderList(ListNode head) {
+ // 使用双端队列的方法来解决
+ Deque de = new LinkedList<>();
+ // 这里是取head的下一个节点,head不需要再入队了,避免造成重复
+ ListNode cur = head.next;
+ while (cur != null){
+ de.offer(cur);
+ cur = cur.next;
+ }
+ cur = head; // 回到头部
+
+ int count = 0;
+ while (!de.isEmpty()){
+ if (count % 2 == 0){
+ // 偶数,取出队列右边尾部的值
+ cur.next = de.pollLast();
+ }else {
+ // 奇数,取出队列左边头部的值
+ cur.next = de.poll();
+ }
+ cur = cur.next;
+ count++;
+ }
+ cur.next = null;
+ }
+}
+
+```
+
+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:
-----------------------
+
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
+
diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md
index aceb91b4..36652109 100644
--- a/problems/0150.逆波兰表达式求值.md
+++ b/problems/0150.逆波兰表达式求值.md
@@ -13,7 +13,7 @@
# 150. 逆波兰表达式求值
-https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
+[力扣题目链接](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/)
根据 逆波兰表示法,求表达式的值。
@@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
-
+
示例 1:
* 输入: ["2", "1", "+", "3", " * "]
@@ -37,16 +37,21 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
示例 3:
* 输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"]
+
* 输出: 22
+
* 解释:该算式转化为常见的中缀算术表达式为:
+
+ ```
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
-= ((10 * (6 / (12 * -11))) + 17) + 5
-= ((10 * (6 / -132)) + 17) + 5
-= ((10 * 0) + 17) + 5
-= (0 + 17) + 5
-= 17 + 5
-= 22
-
+ = ((10 * (6 / (12 * -11))) + 17) + 5
+ = ((10 * (6 / -132)) + 17) + 5
+ = ((10 * 0) + 17) + 5
+ = (0 + 17) + 5
+ = 17 + 5
+ = 22
+ ```
+
逆波兰表达式:是一种后缀表达式,所谓后缀就是指算符写在后面。
@@ -62,7 +67,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
# 思路
-在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)提到了 递归就是用栈来实现的。
+在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)提到了 递归就是用栈来实现的。
所以**栈与递归之间在某种程度上是可以转换的!** 这一点我们在后续讲解二叉树的时候,会更详细的讲解到。
@@ -70,17 +75,17 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后续遍历的方式把二叉树序列化了,就可以了。
-在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)中的对对碰游戏是不是就非常像了。**
+在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)中的对对碰游戏是不是就非常像了。**
如动画所示:

-相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)是差不错的,只不过本题不要相邻元素做消除了,而是做运算!
+相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)是差不错的,只不过本题不要相邻元素做消除了,而是做运算!
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int evalRPN(vector& tokens) {
@@ -223,17 +228,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()) # 如果一开始只有一个数,那么会是字符串形式的
+
```
@@ -241,4 +248,4 @@ def evalRPN(tokens) -> int:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md
index 32d9deef..c4a9c7e0 100644
--- a/problems/0151.翻转字符串里的单词.md
+++ b/problems/0151.翻转字符串里的单词.md
@@ -12,7 +12,7 @@
# 151.翻转字符串里的单词
-https://leetcode-cn.com/problems/reverse-words-in-a-string/
+[力扣题目链接](https://leetcode-cn.com/problems/reverse-words-in-a-string/)
给定一个字符串,逐个翻转字符串中的每个单词。
@@ -61,7 +61,7 @@ https://leetcode-cn.com/problems/reverse-words-in-a-string/
思路很明确了,我们说一说代码的实现细节,就拿移除多余空格来说,一些同学会上来写如下代码:
-```C++
+```CPP
void removeExtraSpaces(string& s) {
for (int i = s.size() - 1; i > 0; i--) {
if (s[i] == s[i - 1] && s[i] == ' ') {
@@ -83,17 +83,17 @@ void removeExtraSpaces(string& s) {
如果不仔细琢磨一下erase的时间复杂读,还以为以上的代码是O(n)的时间复杂度呢。
-想一下真正的时间复杂度是多少,一个erase本来就是O(n)的操作,erase实现原理题目:[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww),最优的算法来移除元素也要O(n)。
+想一下真正的时间复杂度是多少,一个erase本来就是O(n)的操作,erase实现原理题目:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html),最优的算法来移除元素也要O(n)。
erase操作上面还套了一个for循环,那么以上代码移除冗余空格的代码时间复杂度为O(n^2)。
那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。
-如果对这个操作比较生疏了,可以再看一下这篇文章:[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww)是如何移除元素的。
+如果对这个操作比较生疏了,可以再看一下这篇文章:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)是如何移除元素的。
那么使用双指针来移除冗余空格代码如下: fastIndex走的快,slowIndex走的慢,最后slowIndex就标记着移除多余空格后新字符串的长度。
-```C++
+```CPP
void removeExtraSpaces(string& s) {
int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
// 去掉字符串前面的空格
@@ -125,7 +125,7 @@ void removeExtraSpaces(string& s) {
此时我们已经实现了removeExtraSpaces函数来移除冗余空格。
-还做实现反转字符串的功能,支持反转字符串子区间,这个实现我们分别在[344.反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)和[541.反转字符串II](https://mp.weixin.qq.com/s/pzXt6PQ029y7bJ9YZB2mVQ)里已经讲过了。
+还做实现反转字符串的功能,支持反转字符串子区间,这个实现我们分别在[344.反转字符串](https://programmercarl.com/0344.反转字符串.html)和[541.反转字符串II](https://programmercarl.com/0541.反转字符串II.html)里已经讲过了。
代码如下:
@@ -141,7 +141,7 @@ void reverse(string& s, int start, int end) {
本题C++整体代码
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -467,6 +467,85 @@ function reverse(strArr, start, end) {
}
```
+Swift:
+
+```swift
+func reverseWords(_ s: String) -> String {
+ var stringArr = removeSpace(s)
+ reverseString(&stringArr, startIndex: 0, endIndex: stringArr.count - 1)
+ reverseWord(&stringArr)
+ return String(stringArr)
+}
+
+/// 1、移除多余的空格(前后所有的空格,中间只留一个空格)
+func removeSpace(_ s: String) -> [Character] {
+ let ch = Array(s)
+ var left = 0
+ var right = ch.count - 1
+ // 忽略字符串前面的所有空格
+ while ch[left] == " " {
+ left += 1
+ }
+ // 忽略字符串后面的所有空格
+ while ch[right] == " " {
+ right -= 1
+ }
+
+ // 接下来就是要处理中间的多余空格
+ var lastArr = Array()
+ while left <= right {
+ // 准备加到新字符串当中的字符
+ let char = ch[left]
+ // 新的字符串的最后一个字符;或者原字符串中,准备加到新字符串的那个字符;这两个字符当中,只要有一个不是空格,就可以加到新的字符串当中
+ if char != " " || lastArr[lastArr.count - 1] != " " {
+ lastArr.append(char)
+ }
+
+ left += 1
+ }
+ return lastArr
+}
+
+/// 2、反转整个字符串
+func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) {
+ var start = startIndex
+ var end = endIndex
+ while start < end {
+ (s[start], s[end]) = (s[end], s[start])
+ start += 1
+ end -= 1
+ }
+}
+
+/// 3、再次将字符串里面的单词反转
+func reverseWord(_ s: inout [Character]) {
+ var start = 0
+ var end = 0
+ var entry = false
+
+ for i in 0..
+
diff --git a/problems/0160.相交链表.md b/problems/0160.相交链表.md
index d26f66fd..42b2ee56 100644
--- a/problems/0160.相交链表.md
+++ b/problems/0160.相交链表.md
@@ -1,2 +1,3 @@
-同:[链表:链表相交](./面试题02.07.链表相交.md)
+同:[链表:链表相交](https://programmercarl.com/面试题02.07.链表相交.html)
+
diff --git a/problems/0188.买卖股票的最佳时机IV.md b/problems/0188.买卖股票的最佳时机IV.md
index 46c6f7f0..bcb8a1ab 100644
--- a/problems/0188.买卖股票的最佳时机IV.md
+++ b/problems/0188.买卖股票的最佳时机IV.md
@@ -8,7 +8,7 @@
## 188.买卖股票的最佳时机IV
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/)
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
@@ -35,13 +35,13 @@
## 思路
-这道题目可以说是[动态规划:123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)的进阶版,这里要求至多有k次交易。
+这道题目可以说是[动态规划:123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)的进阶版,这里要求至多有k次交易。
动规五部曲,分析如下:
1. 确定dp数组以及下标的含义
-在[动态规划:123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)中,我是定义了一个二维dp数组,本题其实依然可以用一个二维dp数组。
+在[动态规划:123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)中,我是定义了一个二维dp数组,本题其实依然可以用一个二维dp数组。
使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]
@@ -84,14 +84,14 @@ vector> dp(prices.size(), vector(2 * k + 1, 0));
同理可以类比剩下的状态,代码如下:
-```C++
+```CPP
for (int j = 0; j < 2 * k - 1; j += 2) {
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
}
```
-**本题和[动态规划:123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)最大的区别就是这里要类比j为奇数是买,偶数是卖剩的状态**。
+**本题和[动态规划:123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)最大的区别就是这里要类比j为奇数是买,偶数是卖剩的状态**。
3. dp数组如何初始化
@@ -117,7 +117,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) {
代码如下:
-```C++
+```CPP
for (int j = 1; j < 2 * k; j += 2) {
dp[0][j] = -prices[0];
}
@@ -139,7 +139,7 @@ for (int j = 1; j < 2 * k; j += 2) {
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(int k, vector& prices) {
@@ -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 {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0189.旋转数组.md b/problems/0189.旋转数组.md
index 9f565c1d..70aec5fe 100644
--- a/problems/0189.旋转数组.md
+++ b/problems/0189.旋转数组.md
@@ -37,17 +37,17 @@
这道题目在字符串里其实很常见,我把字符串反转相关的题目列一下:
-* [字符串:力扣541.反转字符串II](https://mp.weixin.qq.com/s/pzXt6PQ029y7bJ9YZB2mVQ)
-* [字符串:力扣151.翻转字符串里的单词](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)
-* [字符串:剑指Offer58-II.左旋转字符串](https://mp.weixin.qq.com/s/Px_L-RfT2b_jXKcNmccPsw)
+* [字符串:力扣541.反转字符串II](https://programmercarl.com/0541.反转字符串II.html)
+* [字符串:力扣151.翻转字符串里的单词](https://programmercarl.com/0151.翻转字符串里的单词.html)
+* [字符串:剑指Offer58-II.左旋转字符串](https://programmercarl.com/剑指Offer58-II.左旋转字符串.html)
-本题其实和[字符串:剑指Offer58-II.左旋转字符串](https://mp.weixin.qq.com/s/Px_L-RfT2b_jXKcNmccPsw)就非常像了,剑指offer上左旋转,本题是右旋转。
+本题其实和[字符串:剑指Offer58-II.左旋转字符串](https://programmercarl.com/剑指Offer58-II.左旋转字符串.html)就非常像了,剑指offer上左旋转,本题是右旋转。
注意题目要求是**要求使用空间复杂度为 O(1) 的 原地 算法**
那么我来提供一种旋转的方式哈。
-在[字符串:剑指Offer58-II.左旋转字符串](https://mp.weixin.qq.com/s/Px_L-RfT2b_jXKcNmccPsw)中,我们提到,如下步骤就可以坐旋转字符串:
+在[字符串:剑指Offer58-II.左旋转字符串](https://programmercarl.com/剑指Offer58-II.左旋转字符串.html)中,我们提到,如下步骤就可以坐旋转字符串:
1. 反转区间为前n的子串
2. 反转区间为n到末尾的子串
@@ -69,7 +69,7 @@
C++代码如下:
-```C++
+```CPP
class Solution {
public:
void rotate(vector& nums, int k) {
@@ -131,12 +131,28 @@ class Solution:
## JavaScript
```js
+var rotate = function (nums, k) {
+ function reverse(nums, i, j) {
+ while (i < j) {
+ [nums[i],nums[j]] = [nums[j],nums[i]]; // 解构赋值
+ i++;
+ j--;
+ }
+ }
+ let n = nums.length;
+ k %= n;
+ if (k) {
+ reverse(nums, 0, n - 1);
+ reverse(nums, 0, k - 1);
+ reverse(nums, k, n - 1);
+ }
+};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md
index 63a68c36..c8645c48 100644
--- a/problems/0198.打家劫舍.md
+++ b/problems/0198.打家劫舍.md
@@ -8,7 +8,7 @@
## 198.打家劫舍
-题目链接:https://leetcode-cn.com/problems/house-robber/
+[力扣题目链接](https://leetcode-cn.com/problems/house-robber/)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
@@ -25,7 +25,7 @@
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
-
+
提示:
@@ -59,7 +59,7 @@
代码如下:
-```C++
+```CPP
vector dp(nums.size());
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
@@ -70,7 +70,7 @@ dp[1] = max(nums[0], nums[1]);
dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历!
代码如下:
-```C++
+```CPP
for (int i = 2; i < nums.size(); i++) {
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
@@ -86,7 +86,7 @@ for (int i = 2; i < nums.size(); i++) {
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int rob(vector& nums) {
@@ -175,6 +175,22 @@ func max(a, b int) int {
}
```
+JavaScript:
+
+```javascript
+const rob = nums => {
+ // 数组长度
+ const len = nums.length;
+ // dp数组初始化
+ const dp = [nums[0], Math.max(nums[0], nums[1])];
+ // 从下标2开始遍历
+ for (let i = 2; i < len; i++) {
+ dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
+ }
+ return dp[len - 1];
+};
+```
+
@@ -182,4 +198,4 @@ func max(a, b int) int {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0202.快乐数.md b/problems/0202.快乐数.md
index d1bccd64..710c824d 100644
--- a/problems/0202.快乐数.md
+++ b/problems/0202.快乐数.md
@@ -12,7 +12,7 @@
# 第202题. 快乐数
-https://leetcode-cn.com/problems/happy-number/
+[力扣题目链接](https://leetcode-cn.com/problems/happy-number/)
编写一个算法来判断一个数 n 是不是快乐数。
@@ -36,7 +36,7 @@ https://leetcode-cn.com/problems/happy-number/
题目中说了会 **无限循环**,那么也就是说**求和的过程中,sum会重复出现,这对解题很重要!**
-正如:[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中所说,**当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。**
+正如:[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)中所说,**当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。**
所以这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
@@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/happy-number/
C++代码如下:
-```C++
+```CPP
class Solution {
public:
// 取数值各个位上的单数之和
@@ -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:
@@ -187,10 +191,71 @@ var isHappy = function(n) {
};
```
+Swift:
+```swift
+// number 每个位置上的数字的平方和
+func getSum(_ number: Int) -> Int {
+ var sum = 0
+ var num = number
+ while num > 0 {
+ let temp = num % 10
+ sum += (temp * temp)
+ num /= 10
+ }
+ return sum
+}
+func isHappy(_ n: Int) -> Bool {
+ var set = Set()
+ var num = n
+ while true {
+ let sum = self.getSum(num)
+ if sum == 1 {
+ return true
+ }
+ // 如果这个sum曾经出现过,说明已经陷入了无限循环了
+ if set.contains(sum) {
+ return false
+ } else {
+ set.insert(sum)
+ }
+ num = sum
+ }
+}
+```
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer $n
+ * @return Boolean
+ */
+ function isHappy($n) {
+ // use a set to record sum
+ // whenever there is a duplicated, stop
+ // == 1 return true, else false
+ $table = [];
+ while ($n != 1 && !isset($table[$n])) {
+ $table[$n] = 1;
+ $n = self::getNextN($n);
+ }
+ return $n == 1;
+ }
+
+ function getNextN(int $n) {
+ $res = 0;
+ while ($n > 0) {
+ $temp = $n % 10;
+ $res += $temp * $temp;
+ $n = floor($n / 10);
+ }
+ return $res;
+ }
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md
index ea1d705a..c4f187e8 100644
--- a/problems/0203.移除链表元素.md
+++ b/problems/0203.移除链表元素.md
@@ -11,7 +11,7 @@
# 203.移除链表元素
-https://leetcode-cn.com/problems/remove-linked-list-elements/
+[力扣题目链接](https://leetcode-cn.com/problems/remove-linked-list-elements/)
题意:删除链表中等于给定值 val 的所有节点。
@@ -89,7 +89,7 @@ https://leetcode-cn.com/problems/remove-linked-list-elements/
**直接使用原来的链表来进行移除节点操作:**
-```C++
+```CPP
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
@@ -118,7 +118,7 @@ public:
**设置一个虚拟头结点在进行移除节点操作:**
-```C++
+```CPP
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
@@ -304,11 +304,62 @@ 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
+}
+```
-
+PHP:
+```php
+/**
+ * Definition for singly-linked list.
+ * type ListNode struct {
+ * Val int
+ * Next *ListNode
+ * }
+ */
+ // 虚拟头+双指针
+func removeElements(head *ListNode, val int) *ListNode {
+ dummyHead := &ListNode{}
+ dummyHead.Next = head
+ pred := dummyHead
+ cur := head
+ for cur != nil {
+ if cur.Val == val {
+ pred.Next = cur.Next
+ } else {
+ pred = cur
+ }
+ cur = cur.Next
+ }
+ return dummyHead.Next
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0205.同构字符串.md b/problems/0205.同构字符串.md
index 4e963ece..5d20aa4a 100644
--- a/problems/0205.同构字符串.md
+++ b/problems/0205.同构字符串.md
@@ -9,7 +9,7 @@
# 205. 同构字符串
-题目地址:https://leetcode-cn.com/problems/isomorphic-strings/
+[力扣题目链接](https://leetcode-cn.com/problems/isomorphic-strings/)
给定两个字符串 s 和 t,判断它们是否是同构的。
@@ -39,7 +39,7 @@
C++代码 如下:
-```C++
+```CPP
class Solution {
public:
bool isIsomorphic(string s, string t) {
@@ -68,6 +68,25 @@ public:
## Java
```java
+class Solution {
+ public boolean isIsomorphic(String s, String t) {
+ Map map1 = new HashMap<>();
+ Map map2 = new HashMap<>();
+ for (int i = 0, j = 0; i < s.length(); i++, j++) {
+ if (!map1.containsKey(s.charAt(i))) {
+ map1.put(s.charAt(i), t.charAt(j)); // map1保存 s[i] 到 t[j]的映射
+ }
+ if (!map2.containsKey(t.charAt(j))) {
+ map2.put(t.charAt(j), s.charAt(i)); // map2保存 t[j] 到 s[i]的映射
+ }
+ // 无法映射,返回 false
+ if (map1.get(s.charAt(i)) != t.charAt(j) || map2.get(t.charAt(j)) != s.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
```
## Python
@@ -78,6 +97,23 @@ public:
## Go
```go
+func isIsomorphic(s string, t string) bool {
+ map1 := make(map[byte]byte)
+ map2 := make(map[byte]byte)
+ for i := range s {
+ if _, ok := map1[s[i]]; !ok {
+ map1[s[i]] = t[i] // map1保存 s[i] 到 t[j]的映射
+ }
+ if _, ok := map2[t[i]]; !ok {
+ map2[t[i]] = s[i] // map2保存 t[i] 到 s[j]的映射
+ }
+ // 无法映射,返回 false
+ if (map1[s[i]] != t[i]) || (map2[t[i]] != s[i]) {
+ return false
+ }
+ }
+ return true
+}
```
## JavaScript
@@ -89,5 +125,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md
index 963d7916..45196386 100644
--- a/problems/0206.翻转链表.md
+++ b/problems/0206.翻转链表.md
@@ -11,7 +11,7 @@
# 206.反转链表
-https://leetcode-cn.com/problems/reverse-linked-list/
+[力扣题目链接](https://leetcode-cn.com/problems/reverse-linked-list/)
题意:反转一个单链表。
@@ -48,7 +48,7 @@ https://leetcode-cn.com/problems/reverse-linked-list/
# C++代码
## 双指针法
-```C++
+```CPP
class Solution {
public:
ListNode* reverseList(ListNode* head) {
@@ -74,7 +74,7 @@ public:
关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。
具体可以看代码(已经详细注释),**双指针法写出来之后,理解如下递归写法就不难了,代码逻辑都是一样的。**
-```C++
+```CPP
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
@@ -275,11 +275,104 @@ var reverseList = function(head) {
};
```
+Ruby:
+```ruby
+# 双指针
+# Definition for singly-linked list.
+# class ListNode
+# attr_accessor :val, :next
+# def initialize(val = 0, _next = nil)
+# @val = val
+# @next = _next
+# end
+# end
+def reverse_list(head)
+ # return nil if head.nil? # 循环判断条件亦能起到相同作用因此不必单独判断
+ cur, per = head, nil
+ until cur.nil?
+ tem = cur.next
+ cur.next = per
+ per = cur
+ cur = tem
+ end
+ per
+end
+# 递归
+# Definition for singly-linked list.
+# class ListNode
+# attr_accessor :val, :next
+# def initialize(val = 0, _next = nil)
+# @val = val
+# @next = _next
+# end
+# end
+def reverse_list(head)
+ reverse(nil, head)
+end
+
+def reverse(pre, cur)
+ return pre if cur.nil?
+ tem = cur.next
+ cur.next = pre
+ reverse(cur, tem) # 通过递归实现双指针法中的更新操作
+end
+```
+Kotlin:
+```Kotlin
+fun reverseList(head: ListNode?): ListNode? {
+ var pre: ListNode? = null
+ var cur = head
+ while (cur != null) {
+ val temp = cur.next
+ cur.next = pre
+ pre = cur
+ cur = temp
+ }
+ return pre
+}
+```
+
+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)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0209.长度最小的子数组.md b/problems/0209.长度最小的子数组.md
index 42687514..7c3fd0e7 100644
--- a/problems/0209.长度最小的子数组.md
+++ b/problems/0209.长度最小的子数组.md
@@ -9,7 +9,7 @@
## 209.长度最小的子数组
-题目链接: https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+[力扣题目链接](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
@@ -26,7 +26,7 @@
代码如下:
-```C++
+```CPP
class Solution {
public:
int minSubArrayLen(int s, vector& nums) {
@@ -86,7 +86,7 @@ public:
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int minSubArrayLen(int s, vector& nums) {
@@ -216,8 +216,85 @@ var minSubArrayLen = function(target, nums) {
};
```
+Swift:
+
+```swift
+func minSubArrayLen(_ target: Int, _ nums: [Int]) -> Int {
+ var result = Int.max
+ var sum = 0
+ var starIndex = 0
+ for endIndex in 0..= target {
+ result = min(result, endIndex - starIndex + 1)
+ sum -= nums[starIndex]
+ starIndex += 1
+ }
+ }
+
+ return result == Int.max ? 0 : result
+}
+```
+
+Rust:
+
+```rust
+impl Solution {
+ pub fn min_sub_array_len(target: i32, nums: Vec) -> i32 {
+ let (mut result, mut subLength): (i32, i32) = (i32::MAX, 0);
+ let (mut sum, mut i) = (0, 0);
+
+ for (pos, val) in nums.iter().enumerate() {
+ sum += val;
+ while sum >= target {
+ subLength = (pos - i + 1) as i32;
+ if result > subLength {
+ result = subLength;
+ }
+ sum -= nums[i];
+ i += 1;
+ }
+ }
+ if result == i32::MAX {
+ return 0;
+ }
+ result
+ }
+}
+```
+
+PHP:
+```php
+// 双指针 - 滑动窗口
+class Solution {
+ /**
+ * @param Integer $target
+ * @param Integer[] $nums
+ * @return Integer
+ */
+ function minSubArrayLen($target, $nums) {
+ if (count($nums) < 1) {
+ return 0;
+ }
+ $sum = 0;
+ $res = PHP_INT_MAX;
+ $left = 0;
+ for ($right = 0; $right < count($nums); $right++) {
+ $sum += $nums[$right];
+ while ($sum >= $target) {
+ $res = min($res, $right - $left + 1);
+ $sum -= $nums[$left];
+ $left++;
+ }
+ }
+ return $res == PHP_INT_MAX ? 0 : $res;
+ }
+}
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md
index 12951117..332d3218 100644
--- a/problems/0213.打家劫舍II.md
+++ b/problems/0213.打家劫舍II.md
@@ -8,7 +8,7 @@
## 213.打家劫舍II
-题目链接:https://leetcode-cn.com/problems/house-robber-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/house-robber-ii/)
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
@@ -28,14 +28,14 @@
示例 3:
输入:nums = [0]
输出:0
-
+
提示:
* 1 <= nums.length <= 100
* 0 <= nums[i] <= 1000
## 思路
-这道题目和[198.打家劫舍](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)是差不多的,唯一区别就是成环了。
+这道题目和[198.打家劫舍](https://programmercarl.com/0198.打家劫舍.html)是差不多的,唯一区别就是成环了。
对于一个数组,成环的话主要有如下三种情况:
@@ -55,11 +55,11 @@
**而情况二 和 情况三 都包含了情况一了,所以只考虑情况二和情况三就可以了**。
-分析到这里,本题其实比较简单了。 剩下的和[198.打家劫舍](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)就是一样的了。
+分析到这里,本题其实比较简单了。 剩下的和[198.打家劫舍](https://programmercarl.com/0198.打家劫舍.html)就是一样的了。
代码如下:
-```C++
+```CPP
// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
class Solution {
public:
@@ -174,4 +174,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md
index 15220464..10491553 100644
--- a/problems/0216.组合总和III.md
+++ b/problems/0216.组合总和III.md
@@ -13,7 +13,7 @@
# 216.组合总和III
-链接:https://leetcode-cn.com/problems/combination-sum-iii/
+[力扣题目链接](https://leetcode-cn.com/problems/combination-sum-iii/)
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
@@ -34,9 +34,9 @@
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
-相对于[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ),无非就是多了一个限制,本题是要找到和为n的k个数的组合,而整个集合已经是固定的了[1,...,9]。
+相对于[77. 组合](https://programmercarl.com/0077.组合.html),无非就是多了一个限制,本题是要找到和为n的k个数的组合,而整个集合已经是固定的了[1,...,9]。
-想到这一点了,做过[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)之后,本题是简单一些了。
+想到这一点了,做过[77. 组合](https://programmercarl.com/0077.组合.html)之后,本题是简单一些了。
本题k相当于了树的深度,9(因为整个集合就是9个数)就是树的宽度。
@@ -53,7 +53,7 @@
* **确定递归函数参数**
-和[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。
+和[77. 组合](https://programmercarl.com/0077.组合.html)一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。
这里我依然定义path 和 result为全局变量。
@@ -94,7 +94,7 @@ void backtracking(int targetSum, int k, int sum, int startIndex)
所以 终止代码如下:
-```C++
+```CPP
if (path.size() == k) {
if (sum == targetSum) result.push_back(path);
return; // 如果path.size() == k 但sum != targetSum 直接返回
@@ -103,7 +103,7 @@ if (path.size() == k) {
* **单层搜索过程**
-本题和[77. 组合](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)区别之一就是集合固定的就是9个数[1,...,9],所以for循环固定i<=9
+本题和[77. 组合](https://programmercarl.com/0077.组合.html)区别之一就是集合固定的就是9个数[1,...,9],所以for循环固定i<=9
如图:

@@ -112,7 +112,7 @@ if (path.size() == k) {
代码如下:
-```C++
+```CPP
for (int i = startIndex; i <= 9; i++) {
sum += i;
path.push_back(i);
@@ -124,9 +124,9 @@ for (int i = startIndex; i <= 9; i++) {
**别忘了处理过程 和 回溯过程是一一对应的,处理有加,回溯就要有减!**
-参照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中的模板,不难写出如下C++代码:
+参照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中的模板,不难写出如下C++代码:
-```C++
+```CPP
class Solution {
private:
vector> result; // 存放结果集
@@ -176,7 +176,7 @@ if (sum > targetSum) { // 剪枝操作
}
```
-和[回溯算法:组合问题再剪剪枝](https://mp.weixin.qq.com/s/Ri7spcJMUmph4c6XjPWXQA) 一样,for循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了。
+和[回溯算法:组合问题再剪剪枝](https://programmercarl.com/0077.组合优化.html) 一样,for循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了。
最后C++代码如下:
@@ -214,7 +214,7 @@ public:
# 总结
-开篇就介绍了本题与[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)的区别,相对来说加了元素总和的限制,如果做完[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)再做本题在合适不过。
+开篇就介绍了本题与[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)的区别,相对来说加了元素总和的限制,如果做完[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)再做本题在合适不过。
分析完区别,依然把问题抽象为树形结构,按照回溯三部曲进行讲解,最后给出剪枝的优化。
@@ -392,10 +392,66 @@ var combinationSum3 = function(k, n) {
};
```
+C:
+```c
+int* path;
+int pathTop;
+int** ans;
+int ansTop;
+int getPathSum() {
+ int i;
+ int sum = 0;
+ for(i = 0; i < pathTop; i++) {
+ sum += path[i];
+ }
+ return sum;
+}
+void backtracking(int targetSum, int k, int sum, int startIndex) {
+ if(pathTop == k) {
+ if(sum == targetSum) {
+ int* tempPath = (int*)malloc(sizeof(int) * k);
+ int j;
+ for(j = 0; j < k; j++)
+ tempPath[j] = path[j];
+ ans[ansTop++] = tempPath;
+ }
+ // 如果path.size() == k 但sum != targetSum 直接返回
+ return;
+ }
+ int i;
+ //从startIndex开始遍历,一直遍历到9
+ for (i = startIndex; i <= 9; i++) {
+ sum += i; // 处理
+ path[pathTop++] = i; // 处理
+ backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndex
+ sum -= i; // 回溯
+ pathTop--;; // 回溯
+ }
+}
+
+int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes){
+ //初始化辅助变量
+ path = (int*)malloc(sizeof(int) * k);
+ ans = (int**)malloc(sizeof(int*) * 20);
+ pathTop = ansTop = 0;
+
+ backtracking(n, k, 0, 1);
+
+ //设置返回的二维数组中元素个数为ansTop
+ *returnSize = ansTop;
+ //设置二维数组中每个元素个数的大小为k
+ *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
+ int i;
+ for(i = 0; i < ansTop; i++) {
+ (*returnColumnSizes)[i] = k;
+ }
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0222.完全二叉树的节点个数.md b/problems/0222.完全二叉树的节点个数.md
index ec68b6c6..13017f7f 100644
--- a/problems/0222.完全二叉树的节点个数.md
+++ b/problems/0222.完全二叉树的节点个数.md
@@ -7,24 +7,23 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 222.完全二叉树的节点个数
+# 222.完全二叉树的节点个数
-题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/
+[力扣题目链接](https://leetcode-cn.com/problems/count-complete-tree-nodes/)
给出一个完全二叉树,求出该树的节点个数。
-示例:
示例 1:
-输入:root = [1,2,3,4,5,6]
-输出:6
+* 输入:root = [1,2,3,4,5,6]
+* 输出:6
示例 2:
-输入:root = []
-输出:0
+* 输入:root = []
+* 输出:0
示例 3:
-输入:root = [1]
-输出:1
+* 输入:root = [1]
+* 输出:1
提示:
@@ -33,21 +32,22 @@
* 题目数据保证输入的树是 完全二叉树
-## 思路
+# 思路
本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。
+
## 普通二叉树
首先按照普通二叉树的逻辑来求。
-这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
+这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
递归遍历的顺序依然是后序(左右中)。
### 递归
-如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)。
+如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)。
1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。
@@ -77,7 +77,7 @@ return treeNum;
所以整体C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
private:
@@ -96,7 +96,7 @@ public:
```
代码精简之后C++代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -107,19 +107,19 @@ public:
};
```
-时间复杂度:O(n)
-空间复杂度:O(logn),算上了递归系统栈占用的空间
+* 时间复杂度:O(n)
+* 空间复杂度:O(logn),算上了递归系统栈占用的空间
**网上基本都是这个精简的代码版本,其实不建议大家照着这个来写,代码确实精简,但隐藏了一些内容,连遍历的顺序都看不出来,所以初学者建议学习版本一的代码,稳稳的打基础**。
### 迭代法
-如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)。
+如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)。
那么只要模板少做改动,加一个变量result,统计节点数量就可以了
-```C++
+```CPP
class Solution {
public:
int countNodes(TreeNode* root) {
@@ -140,12 +140,12 @@ public:
}
};
```
-时间复杂度:O(n)
-空间复杂度:O(n)
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
## 完全二叉树
-以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A),这篇详细介绍了各种二叉树的特性。
+以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://programmercarl.com/二叉树理论基础.html),这篇详细介绍了各种二叉树的特性。
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
@@ -163,7 +163,7 @@ public:
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int countNodes(TreeNode* root) {
@@ -187,13 +187,12 @@ public:
};
```
-时间复杂度:O(logn * logn)
-空间复杂度:O(logn)
+* 时间复杂度:O(logn * logn)
+* 空间复杂度:O(logn)
-## 其他语言版本
+# 其他语言版本
-
-Java:
+## Java
```java
class Solution {
// 通用递归解法
@@ -205,7 +204,27 @@ class Solution {
}
}
```
-
+```java
+class Solution {
+ // 迭代法
+ public int countNodes(TreeNode root) {
+ if (root == null) return 0;
+ Queue queue = new LinkedList<>();
+ queue.offer(root);
+ int result = 0;
+ while (!queue.isEmpty()) {
+ int size = queue.size();
+ while (size -- > 0) {
+ TreeNode cur = queue.poll();
+ result++;
+ if (cur.left != null) queue.offer(cur.left);
+ if (cur.right != null) queue.offer(cur.right);
+ }
+ }
+ return result;
+ }
+}
+```
```java
class Solution {
/**
@@ -238,9 +257,9 @@ class Solution {
}
```
-Python:
+## Python
-> 递归法:
+递归法:
```python
class Solution:
def countNodes(self, root: TreeNode) -> int:
@@ -255,7 +274,7 @@ class Solution:
return treeNum
```
-> 递归法:精简版
+递归法:精简版
```python
class Solution:
def countNodes(self, root: TreeNode) -> int:
@@ -264,7 +283,7 @@ class Solution:
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
```
-> 迭代法:
+迭代法:
```python
import collections
class Solution:
@@ -285,7 +304,7 @@ class Solution:
return result
```
-> 完全二叉树
+完全二叉树
```python
class Solution:
def countNodes(self, root: TreeNode) -> int:
@@ -306,7 +325,7 @@ class Solution:
return self.countNodes(root.left) + self.countNodes(root.right) + 1
```
-Go:
+## Go
递归版本
@@ -361,7 +380,7 @@ func countNodes(root *TreeNode) int {
-JavaScript:
+## JavaScript:
递归版本
```javascript
@@ -436,4 +455,4 @@ var countNodes = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md
index b3e851a1..8d4db953 100644
--- a/problems/0225.用队列实现栈.md
+++ b/problems/0225.用队列实现栈.md
@@ -12,7 +12,7 @@
# 225. 用队列实现栈
-https://leetcode-cn.com/problems/implement-stack-using-queues/
+[力扣题目链接](https://leetcode-cn.com/problems/implement-stack-using-queues/)
使用队列实现栈的下列操作:
@@ -34,7 +34,7 @@ https://leetcode-cn.com/problems/implement-stack-using-queues/
有的同学可能疑惑这种题目有什么实际工程意义,**其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样!**
-刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://mp.weixin.qq.com/s/Cj6R0qu8rFA7Et9V_ZMjCA)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行!
+刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://programmercarl.com/0232.用栈实现队列.html)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行!
**队列模拟栈,其实一个队列就够了**,那么我们先说一说两个队列来实现栈的思路。
@@ -65,7 +65,7 @@ queue.empty();
详细如代码注释所示:
-```C++
+```CPP
class MyStack {
public:
queue que1;
@@ -118,7 +118,7 @@ public:
C++优化代码
-```C++
+```CPP
class MyStack {
public:
queue que;
@@ -294,58 +294,136 @@ 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
```
Go:
+```go
+type MyStack struct {
+ queue []int//创建一个队列
+}
+
+
+/** Initialize your data structure here. */
+func Constructor() MyStack {
+ return MyStack{ //初始化
+ queue:make([]int,0),
+ }
+}
+
+
+/** Push element x onto stack. */
+func (this *MyStack) Push(x int) {
+ //添加元素
+ this.queue=append(this.queue,x)
+}
+
+
+/** Removes the element on top of the stack and returns that element. */
+func (this *MyStack) Pop() int {
+ n:=len(this.queue)-1//判断长度
+ for n!=0{ //除了最后一个,其余的都重新添加到队列里
+ val:=this.queue[0]
+ this.queue=this.queue[1:]
+ this.queue=append(this.queue,val)
+ n--
+ }
+ //弹出元素
+ val:=this.queue[0]
+ this.queue=this.queue[1:]
+ return val
+
+}
+
+
+/** Get the top element. */
+func (this *MyStack) Top() int {
+ //利用Pop函数,弹出来的元素重新添加
+ val:=this.Pop()
+ this.queue=append(this.queue,val)
+ return val
+}
+
+
+/** Returns whether the stack is empty. */
+func (this *MyStack) Empty() bool {
+ return len(this.queue)==0
+}
+
+
+/**
+ * Your MyStack object will be instantiated and called as such:
+ * obj := Constructor();
+ * obj.Push(x);
+ * param_2 := obj.Pop();
+ * param_3 := obj.Top();
+ * param_4 := obj.Empty();
+ */
+```
+
+
+
javaScript:
使用数组(push, shift)模拟队列
@@ -460,4 +538,4 @@ MyStack.prototype.empty = function() {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md
index 44f0d3b4..fb5d831a 100644
--- a/problems/0226.翻转二叉树.md
+++ b/problems/0226.翻转二叉树.md
@@ -9,7 +9,7 @@
# 226.翻转二叉树
-题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/invert-binary-tree/)
翻转一棵二叉树。
@@ -43,7 +43,7 @@
**注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果**
-**这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不行,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了**
+**这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了**
那么层序遍历可以不可以呢?**依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!**
@@ -51,7 +51,7 @@
-对于二叉树的递归法的前中后序遍历,已经在[二叉树:前中后序递归遍历](https://mp.weixin.qq.com/s/Ww60X5mIKWdMQV4cN3ejOA)详细讲解了。
+对于二叉树的递归法的前中后序遍历,已经在[二叉树:前中后序递归遍历](https://programmercarl.com/二叉树的递归遍历.html)详细讲解了。
我们下文以前序遍历为例,通过动画来看一下翻转的过程:
@@ -89,7 +89,7 @@ invertTree(root->right);
基于这递归三步法,代码基本写完,C++代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
@@ -106,12 +106,11 @@ public:
### 深度优先遍历
-
-[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)中给出了前中后序迭代方式的写法,所以本地可以很轻松的切出如下迭代法的代码:
+[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)中给出了前中后序迭代方式的写法,所以本地可以很轻松的切出如下迭代法的代码:
C++代码迭代法(前序遍历)
-```C++
+```CPP
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
@@ -129,14 +128,14 @@ public:
}
};
```
-如果这个代码看不懂的话可以在回顾一下[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)。
+如果这个代码看不懂的话可以在回顾一下[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)。
-我们在[二叉树:前中后序迭代方式的统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)中介绍了统一的写法,所以,本题也只需将文中的代码少做修改便可。
+我们在[二叉树:前中后序迭代方式的统一写法](https://programmercarl.com/二叉树的统一迭代法.html)中介绍了统一的写法,所以,本题也只需将文中的代码少做修改便可。
C++代码如下迭代法(前序遍历)
-```C++
+```CPP
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
@@ -162,13 +161,13 @@ public:
};
```
-如果上面这个代码看不懂,回顾一下文章[二叉树:前中后序迭代方式的统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)。
+如果上面这个代码看不懂,回顾一下文章[二叉树:前中后序迭代方式的统一写法](https://programmercarl.com/二叉树的统一迭代法.html)。
### 广度优先遍历
也就是层序遍历,层数遍历也是可以翻转这棵树的,因为层序遍历也可以把每个节点的左右孩子都翻转一遍,代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
@@ -188,7 +187,7 @@ public:
}
};
```
-如果对以上代码不理解,或者不清楚二叉树的层序遍历,可以看这篇[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)
+如果对以上代码不理解,或者不清楚二叉树的层序遍历,可以看这篇[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)
## 拓展
@@ -196,7 +195,7 @@ public:
如果非要使用递归中序的方式写,也可以,如下代码就可以避免节点左右孩子翻转两次的情况:
-```C++
+```CPP
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
@@ -215,7 +214,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
@@ -364,7 +363,9 @@ class Solution:
return root
```
-### Go
+### Go
+
+递归版本的前序遍历
```Go
func invertTree(root *TreeNode) *TreeNode {
@@ -382,6 +383,96 @@ func invertTree(root *TreeNode) *TreeNode {
}
```
+递归版本的后序遍历
+
+```go
+func invertTree(root *TreeNode) *TreeNode {
+ if root==nil{
+ return root
+ }
+ invertTree(root.Left)//遍历左节点
+ invertTree(root.Right)//遍历右节点
+ root.Left,root.Right=root.Right,root.Left//交换
+ return root
+}
+```
+
+迭代版本的前序遍历
+
+```go
+func invertTree(root *TreeNode) *TreeNode {
+ stack:=[]*TreeNode{}
+ node:=root
+ for node!=nil||len(stack)>0{
+ for node!=nil{
+ node.Left,node.Right=node.Right,node.Left//交换
+ stack=append(stack,node)
+ node=node.Left
+ }
+ node=stack[len(stack)-1]
+ stack=stack[:len(stack)-1]
+ node=node.Right
+ }
+ return root
+}
+```
+
+迭代版本的后序遍历
+
+```go
+func invertTree(root *TreeNode) *TreeNode {
+ stack:=[]*TreeNode{}
+ node:=root
+ var prev *TreeNode
+ for node!=nil||len(stack)>0{
+ for node!=nil{
+ stack=append(stack,node)
+ node=node.Left
+ }
+ node=stack[len(stack)-1]
+ stack=stack[:len(stack)-1]
+ if node.Right==nil||node.Right==prev{
+ node.Left,node.Right=node.Right,node.Left//交换
+ prev=node
+ node=nil
+ }else {
+ stack=append(stack,node)
+ node=node.Right
+ }
+ }
+ return root
+}
+```
+
+层序遍历
+
+```go
+func invertTree(root *TreeNode) *TreeNode {
+ if root==nil{
+ return root
+ }
+ queue:=list.New()
+ node:=root
+ queue.PushBack(node)
+ for queue.Len()>0{
+ length:=queue.Len()
+ for i:=0;i
+
diff --git a/problems/0232.用栈实现队列.md b/problems/0232.用栈实现队列.md
index 6890fc2b..9f6bb90f 100644
--- a/problems/0232.用栈实现队列.md
+++ b/problems/0232.用栈实现队列.md
@@ -11,7 +11,7 @@
# 232.用栈实现队列
-https://leetcode-cn.com/problems/implement-queue-using-stacks/
+[力扣题目链接](https://leetcode-cn.com/problems/implement-queue-using-stacks/)
使用栈实现队列的下列操作:
@@ -67,7 +67,7 @@ queue.empty();
C++代码如下:
-```C++
+```CPP
class MyQueue {
public:
stack stIn;
@@ -129,101 +129,6 @@ public:
Java:
-使用Stack(堆栈)同名方法:
-```java
-class MyQueue {
- // java中的 Stack 有设计上的缺陷,官方推荐使用 Deque(双端队列) 代替 Stack
- Deque stIn;
- Deque stOut;
- /** Initialize your data structure here. */
- public MyQueue() {
- stIn = new ArrayDeque<>();
- stOut = new ArrayDeque<>();
- }
-
- /** Push element x to the back of queue. */
- public void push(int x) {
- stIn.push(x);
- }
-
- /** Removes the element from in front of queue and returns that element. */
- public int pop() {
- // 只要 stOut 为空,那么就应该将 stIn 中所有的元素倒腾到 stOut 中
- if (stOut.isEmpty()) {
- while (!stIn.isEmpty()) {
- stOut.push(stIn.pop());
- }
- }
- // 再返回 stOut 中的元素
- return stOut.pop();
- }
-
- /** Get the front element. */
- public int peek() {
- // 直接使用已有的pop函数
- int res = this.pop();
- // 因为pop函数弹出了元素res,所以再添加回去
- stOut.push(res);
- return res;
- }
-
- /** Returns whether the queue is empty. */
- public boolean empty() {
- // 当 stIn 栈为空时,说明没有元素可以倒腾到 stOut 栈了
- // 并且 stOut 栈也为空时,说明没有以前从 stIn 中倒腾到的元素了
- return stIn.isEmpty() && stOut.isEmpty();
- }
-}
-```
-
-个人习惯写法,使用Deque通用api:
-```java
-class MyQueue {
- // java中的 Stack 有设计上的缺陷,官方推荐使用 Deque(双端队列) 代替 Stack
- // Deque 中的 addFirst、removeFirst、peekFirst 等方法等效于 Stack(堆栈) 中的 push、pop、peek
- Deque stIn;
- Deque stOut;
- /** Initialize your data structure here. */
- public MyQueue() {
- stIn = new ArrayDeque<>();
- stOut = new ArrayDeque<>();
- }
-
- /** Push element x to the back of queue. */
- public void push(int x) {
- stIn.addLast(x);
- }
-
- /** Removes the element from in front of queue and returns that element. */
- public int pop() {
- // 只要 stOut 为空,那么就应该将 stIn 中所有的元素倒腾到 stOut 中
- if (stOut.isEmpty()) {
- while (!stIn.isEmpty()) {
- stOut.addLast(stIn.pollLast());
- }
- }
- // 再返回 stOut 中的元素
- return stOut.pollLast();
- }
-
- /** Get the front element. */
- public int peek() {
- // 直接使用已有的pop函数
- int res = this.pop();
- // 因为pop函数弹出了元素res,所以再添加回去
- stOut.addLast(res);
- return res;
- }
-
- /** Returns whether the queue is empty. */
- public boolean empty() {
- // 当 stIn 栈为空时,说明没有元素可以倒腾到 stOut 栈了
- // 并且 stOut 栈也为空时,说明没有以前从 stIn 中倒腾到的元素了
- return stIn.isEmpty() && stOut.isEmpty();
- }
-}
-```
-
```java
class MyQueue {
@@ -281,48 +186,53 @@ 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.
"""
- # 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(len(self.stack_in)):
+ self.stack_out.append(self.stack_in.pop())
+ return self.stack_out.pop()
+
def peek(self) -> int:
"""
Get the front element.
"""
- if self.stack2 == []:
- while self.stack1:
- tmp = self.stack1.pop()
- self.stack2.append(tmp)
- return self.stack2[-1]
+ ans = self.pop()
+ self.stack_out.append(ans)
+ return ans
+
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)
+
```
@@ -384,7 +294,7 @@ func (this *MyQueue) Peek() int {
func (this *MyQueue) Empty() bool {
return len(this.stack) == 0 && len(this.back) == 0
}
-
+```
javaScript:
@@ -442,10 +352,8 @@ MyQueue.prototype.empty = function() {
```
-
-
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0234.回文链表.md b/problems/0234.回文链表.md
index 945d2ef4..631d2f6b 100644
--- a/problems/0234.回文链表.md
+++ b/problems/0234.回文链表.md
@@ -9,7 +9,7 @@
# 234.回文链表
-题目链接:https://leetcode-cn.com/problems/palindrome-linked-list/
+[力扣题目链接](https://leetcode-cn.com/problems/palindrome-linked-list/)
请判断一个链表是否为回文链表。
@@ -30,7 +30,7 @@
代码也比较简单。如下:
-```C++
+```CPP
class Solution {
public:
bool isPalindrome(ListNode* head) {
@@ -51,7 +51,7 @@ public:
上面代码可以在优化,就是先求出链表长度,然后给定vector的初始长度,这样避免vector每次添加节点重新开辟空间
-```C++
+```CPP
class Solution {
public:
bool isPalindrome(ListNode* head) {
@@ -95,7 +95,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
bool isPalindrome(ListNode* head) {
@@ -144,21 +144,147 @@ public:
## Java
```java
+// 方法一,使用数组
+class Solution {
+ public boolean isPalindrome(ListNode head) {
+ int len = 0;
+ // 统计链表长度
+ ListNode cur = head;
+ while (cur != null) {
+ len++;
+ cur = cur.next;
+ }
+ cur = head;
+ int[] res = new int[len];
+ // 将元素加到数组之中
+ for (int i = 0; i < res.length; i++){
+ res[i] = cur.val;
+ cur = cur.next;
+ }
+ // 比较回文
+ for (int i = 0, j = len - 1; i < j; i++, j--){
+ if (res[i] != res[j]){
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+// 方法二,快慢指针
+class Solution {
+ public boolean isPalindrome(ListNode head) {
+ // 如果为空或者仅有一个节点,返回true
+ if (head == null && head.next == null) return true;
+ ListNode slow = head;
+ ListNode fast = head;
+ ListNode pre = head;
+ while (fast != null && fast.next != null){
+ pre = slow; // 记录slow的前一个结点
+ slow = slow.next;
+ fast = fast.next.next;
+ }
+ pre.next = null; // 分割两个链表
+
+ // 前半部分
+ ListNode cur1 = head;
+ // 后半部分。这里使用了反转链表
+ ListNode cur2 = reverseList(slow);
+
+ while (cur1 != null){
+ if (cur1.val != cur2.val) return false;
+
+ // 注意要移动两个结点
+ cur1 = cur1.next;
+ cur2 = cur2.next;
+ }
+ return true;
+ }
+ ListNode reverseList(ListNode head){
+ // 反转链表
+ ListNode tmp = null;
+ ListNode pre = null;
+ while (head != null){
+ tmp = head.next;
+ head.next = pre;
+ pre = head;
+ head = tmp;
+ }
+ return pre;
+ }
+}
```
## 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
```go
+
```
## JavaScript
```js
+
```
@@ -166,5 +292,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md
index dffc89e6..25bbf7e4 100644
--- a/problems/0235.二叉搜索树的最近公共祖先.md
+++ b/problems/0235.二叉搜索树的最近公共祖先.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 235. 二叉搜索树的最近公共祖先
+# 235. 二叉搜索树的最近公共祖先
-链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/)
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
@@ -21,14 +21,15 @@
示例 1:
-输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
-输出: 6
-解释: 节点 2 和节点 8 的最近公共祖先是 6。
+* 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
+* 输出: 6
+* 解释: 节点 2 和节点 8 的最近公共祖先是 6。
+
示例 2:
-输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
-输出: 2
-解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
+* 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
+* 输出: 2
+* 解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
@@ -36,9 +37,11 @@
* 所有节点的值都是唯一的。
* p、q 为不同节点且均存在于给定的二叉搜索树中。
-## 思路
+# 思路
-做过[二叉树:公共祖先问题](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)题目的同学应该知道,利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。
+
+
+做过[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)题目的同学应该知道,利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。
那么本题是二叉搜索树,二叉搜索树是有序的,那得好好利用一下这个特点。
@@ -48,7 +51,7 @@
理解这一点,本题就很好解了。
-和[二叉树:公共祖先问题](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)不同,普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。
+和[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)不同,普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。
那么我们可以采用前序遍历(其实这里没有中节点的处理逻辑,遍历顺序无所谓了)。
@@ -58,6 +61,7 @@
可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回!
+## 递归法
递归三部曲如下:
@@ -93,7 +97,7 @@ if (cur == NULL) return cur;
代码如下:
-```C++
+```CPP
if (cur->val > p->val && cur->val > q->val) {
TreeNode* left = traversal(cur->left, p, q);
if (left != NULL) {
@@ -105,13 +109,12 @@ if (cur->val > p->val && cur->val > q->val) {
**细心的同学会发现,在这里调用递归函数的地方,把递归函数的返回值left,直接return**。
-在[二叉树:公共祖先问题](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)中,如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树。
+在[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)中,如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树。
搜索一条边的写法:
```
if (递归函数(root->left)) return ;
-
if (递归函数(root->right)) return ;
```
@@ -128,7 +131,7 @@ left与right的逻辑处理;
如果 cur->val 小于 p->val,同时 cur->val 小于 q->val,那么就应该向右遍历(目标区间在右子树)。
-```
+```CPP
if (cur->val < p->val && cur->val < q->val) {
TreeNode* right = traversal(cur->right, p, q);
if (right != NULL) {
@@ -140,14 +143,14 @@ if (cur->val < p->val && cur->val < q->val) {
剩下的情况,就是cur节点在区间(p->val <= cur->val && cur->val <= q->val)或者 (q->val <= cur->val && cur->val <= p->val)中,那么cur就是最近公共祖先了,直接返回cur。
代码如下:
+
```
return cur;
-
```
那么整体递归代码如下:
-```C++
+```CPP
class Solution {
private:
TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
@@ -177,7 +180,7 @@ public:
精简后代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
@@ -192,13 +195,13 @@ public:
## 迭代法
-对于二叉搜索树的迭代法,大家应该在[二叉树:二叉搜索树登场!](https://mp.weixin.qq.com/s/vsKrWRlETxCVsiRr8v_hHg)就了解了。
+对于二叉搜索树的迭代法,大家应该在[二叉树:二叉搜索树登场!](https://programmercarl.com/0700.二叉搜索树中的搜索.html)就了解了。
利用其有序性,迭代的方式还是比较简单的,解题思路在递归中已经分析了。
迭代代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
@@ -216,19 +219,32 @@ public:
灵魂拷问:是不是又被简单的迭代法感动到痛哭流涕?
-## 总结
+# 总结
-对于二叉搜索树的最近祖先问题,其实要比[普通二叉树公共祖先问题](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)简单的多。
+对于二叉搜索树的最近祖先问题,其实要比[普通二叉树公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)简单的多。
不用使用回溯,二叉搜索树自带方向性,可以方便的从上向下查找目标区间,遇到目标区间内的节点,直接返回。
最后给出了对应的迭代法,二叉搜索树的迭代法甚至比递归更容易理解,也是因为其有序性(自带方向性),按照目标区间找就行了。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
+
+递归法:
+```java
+class Solution {
+ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
+ if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
+ if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
+ return root;
+ }
+}
+```
+
+迭代法:
```java
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
@@ -246,15 +262,11 @@ class Solution {
}
```
-Python:
-```python
-# Definition for a binary tree node.
-# class TreeNode:
-# def __init__(self, x):
-# self.val = x
-# self.left = None
-# self.right = None
+## Python
+
+递归法:
+```python
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root: return root //中
@@ -264,18 +276,14 @@ class Solution:
return self.lowestCommonAncestor(root.right,p,q) //右
else: return root
```
-Go:
-> BSL法
+迭代法:
+
+
+## Go
+
+递归法:
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
//利用BSL的性质(前序遍历有序)
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root==nil{return nil}
@@ -287,34 +295,10 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
}
```
-> 普通法
-```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
-//递归会将值层层返回
-func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
- //终止条件
- if root==nil||root.Val==p.Val||root.Val==q.Val{return root}//最后为空或者找到一个值时,就返回这个值
- //后序遍历
- findLeft:=lowestCommonAncestor(root.Left,p,q)
- findRight:=lowestCommonAncestor(root.Right,p,q)
- //处理单层逻辑
- if findLeft!=nil&&findRight!=nil{return root}//说明在root节点的两边
- if findLeft==nil{//左边没找到,就说明在右边找到了
- return findRight
- }else {return findLeft}
-}
-```
+## JavaScript
-JavaScript版本:
-1. 使用递归的方法
+递归法:
```javascript
var lowestCommonAncestor = function(root, p, q) {
// 使用递归的方法
@@ -336,7 +320,8 @@ var lowestCommonAncestor = function(root, p, q) {
return root;
};
```
-2. 使用迭代的方法
+
+迭代法
```javascript
var lowestCommonAncestor = function(root, p, q) {
// 使用迭代的方法
@@ -355,9 +340,8 @@ var lowestCommonAncestor = function(root, p, q) {
```
-
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md
index 7b5deb56..59345a24 100644
--- a/problems/0236.二叉树的最近公共祖先.md
+++ b/problems/0236.二叉树的最近公共祖先.md
@@ -9,9 +9,9 @@
> 本来是打算将二叉树和二叉搜索树的公共祖先问题一起讲,后来发现篇幅过长了,只能先说一说二叉树的公共祖先问题。
-## 236. 二叉树的最近公共祖先
+# 236. 二叉树的最近公共祖先
-题目链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/)
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
@@ -35,7 +35,7 @@
* 所有节点的值都是唯一的。
* p、q 为不同节点且均存在于给定的二叉树中。
-## 思路
+# 思路
遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。
@@ -79,7 +79,7 @@ if (root == q || root == p || root == NULL) return root;
值得注意的是 本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。
-我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg)中说了 递归函数有返回值就是要遍历某一条边,但有返回值也要看如何处理返回值!
+我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://programmercarl.com/0112.路径总和.html)中说了 递归函数有返回值就是要遍历某一条边,但有返回值也要看如何处理返回值!
如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树呢?
@@ -150,7 +150,7 @@ TreeNode* right = lowestCommonAncestor(root->right, p, q);
代码如下:
-```C++
+```CPP
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
@@ -167,7 +167,7 @@ else { // (left == NULL && right == NULL)
整体代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
@@ -188,7 +188,7 @@ public:
稍加精简,代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
@@ -202,7 +202,7 @@ public:
};
```
-## 总结
+# 总结
这道题目刷过的同学未必真正了解这里面回溯的过程,以及结果是如何一层一层传上去的。
@@ -219,10 +219,10 @@ public:
本题没有给出迭代法,因为迭代法不适合模拟回溯的过程。理解递归的解法就够了。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
@@ -249,7 +249,6 @@ class Solution {
```java
// 代码精简版
class Solution {
- TreeNode pre;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root.val == p.val ||root.val == q.val) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
@@ -262,14 +261,9 @@ class Solution {
}
```
-Python:
+## Python
+
```python
-# Definition for a binary tree node.
-# class TreeNode:
-# def __init__(self, x):
-# self.val = x
-# self.left = None
-# self.right = None
//递归
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
@@ -281,7 +275,9 @@ class Solution:
elif not left and right: return right //目标节点是通过right返回的
else: return None //没找到
```
-Go:
+
+## Go
+
```Go
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
// check
@@ -311,7 +307,8 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
}
```
-JavaScript版本:
+## JavaScript
+
```javascript
var lowestCommonAncestor = function(root, p, q) {
// 使用递归的方法
@@ -343,4 +340,4 @@ var lowestCommonAncestor = function(root, p, q) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0239.滑动窗口最大值.md b/problems/0239.滑动窗口最大值.md
index dedc3247..a61e4ca8 100644
--- a/problems/0239.滑动窗口最大值.md
+++ b/problems/0239.滑动窗口最大值.md
@@ -12,7 +12,7 @@
# 239. 滑动窗口最大值
-https://leetcode-cn.com/problems/sliding-window-maximum/
+[力扣题目链接](https://leetcode-cn.com/problems/sliding-window-maximum/)
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
@@ -21,7 +21,7 @@ https://leetcode-cn.com/problems/sliding-window-maximum/
进阶:
你能在线性时间复杂度内解决此题吗?
-
+
提示:
@@ -104,11 +104,11 @@ public:
那么我们用什么数据结构来实现这个单调队列呢?
-使用deque最为合适,在文章[栈与队列:来看看栈和队列不为人知的一面](https://mp.weixin.qq.com/s/HCXfQ_Bhpi63YaX0ZRSnAQ)中,我们就提到了常用的queue在没有指定容器的情况下,deque就是默认底层容器。
+使用deque最为合适,在文章[栈与队列:来看看栈和队列不为人知的一面](https://programmercarl.com/栈与队列理论基础.html)中,我们就提到了常用的queue在没有指定容器的情况下,deque就是默认底层容器。
基于刚刚说过的单调队列pop和push的规则,代码不难实现,如下:
-```C++
+```CPP
class MyQueue { //单调队列(从大到小)
public:
deque que; // 使用deque来实现单调队列
@@ -140,7 +140,7 @@ public:
C++代码如下:
-```C++
+```CPP
class Solution {
private:
class MyQueue { //单调队列(从大到小)
@@ -425,4 +425,4 @@ var maxSlidingWindow = function (nums, k) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0242.有效的字母异位词.md b/problems/0242.有效的字母异位词.md
index 93bba44c..0828d360 100644
--- a/problems/0242.有效的字母异位词.md
+++ b/problems/0242.有效的字母异位词.md
@@ -11,7 +11,7 @@
## 242.有效的字母异位词
-https://leetcode-cn.com/problems/valid-anagram/
+[力扣题目链接](https://leetcode-cn.com/problems/valid-anagram/)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
@@ -35,7 +35,7 @@ https://leetcode-cn.com/problems/valid-anagram/
**数组其实就是一个简单哈希表**,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。
-如果对哈希表的理论基础关于数组,set,map不了解的话可以看这篇:[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)
+如果对哈希表的理论基础关于数组,set,map不了解的话可以看这篇:[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)
需要定义一个多大的数组呢,定一个数组叫做record,大小为26 就可以了,初始化为0,因为字符a到字符z的ASCII也是26个连续的数值。
@@ -61,7 +61,7 @@ https://leetcode-cn.com/problems/valid-anagram/
C++ 代码如下:
-```C++
+```CPP
class Solution {
public:
bool isAnagram(string s, string t) {
@@ -198,6 +198,83 @@ var isAnagram = function(s, t) {
};
```
+Swift:
+```Swift
+func isAnagram(_ s: String, _ t: String) -> Bool {
+ if s.count != t.count {
+ return false
+ }
+ var record = Array(repeating: 0, count: 26)
+ let aUnicodeScalar = "a".unicodeScalars.first!.value
+ for c in s.unicodeScalars {
+ record[Int(c.value - aUnicodeScalar)] += 1
+ }
+ for c in t.unicodeScalars {
+ record[Int(c.value - aUnicodeScalar)] -= 1
+ }
+ for value in record {
+ if value != 0 {
+ return false
+ }
+ }
+ return true
+}
+```
+
+PHP:
+```php
+class Solution {
+ /**
+ * @param String $s
+ * @param String $t
+ * @return Boolean
+ */
+ function isAnagram($s, $t) {
+ if (strlen($s) != strlen($t)) {
+ return false;
+ }
+ $table = [];
+ for ($i = 0; $i < strlen($s); $i++) {
+ if (!isset($table[$s[$i]])) {
+ $table[$s[$i]] = 1;
+ } else {
+ $table[$s[$i]]++;
+ }
+ if (!isset($table[$t[$i]])) {
+ $table[$t[$i]] = -1;
+ } else {
+ $table[$t[$i]]--;
+ }
+ }
+ foreach ($table as $record) {
+ if ($record != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+```
+
+Rust:
+```rust
+impl Solution {
+ pub fn is_anagram(s: String, t: String) -> bool {
+ let mut record = vec![0; 26];
+
+ let baseChar = 'a';
+
+ for byte in s.bytes() {
+ record[byte as usize - baseChar as usize] += 1;
+ }
+ for byte in t.bytes() {
+ record[byte as usize - baseChar as usize] -= 1;
+ }
+
+ record.iter().filter(|x| **x != 0).count() == 0
+ }
+}
+```
## 相关题目
* 383.赎金信
@@ -209,4 +286,4 @@ var isAnagram = function(s, t) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md
index ce596396..e8a98527 100644
--- a/problems/0257.二叉树的所有路径.md
+++ b/problems/0257.二叉树的所有路径.md
@@ -9,9 +9,9 @@
> 以为只用了递归,其实还用了回溯
-## 257. 二叉树的所有路径
+# 257. 二叉树的所有路径
-题目地址:https://leetcode-cn.com/problems/binary-tree-paths/
+[力扣题目链接](https://leetcode-cn.com/problems/binary-tree-paths/)
给定一个二叉树,返回所有从根节点到叶子节点的路径。
@@ -20,7 +20,7 @@
示例:

-## 思路
+# 思路
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
@@ -77,7 +77,7 @@ if (cur->left == NULL && cur->right == NULL) {
这里我们先使用vector结构的path容器来记录路径,那么终止处理逻辑如下:
-```C++
+```CPP
if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点
string sPath;
for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式
@@ -113,7 +113,7 @@ if (cur->right) {
那么回溯要怎么回溯呢,一些同学会这么写,如下:
-```C++
+```CPP
if (cur->left) {
traversal(cur->left, path, result);
}
@@ -129,7 +129,7 @@ path.pop_back();
那么代码应该这么写:
-```C++
+```CPP
if (cur->left) {
traversal(cur->left, path, result);
path.pop_back(); // 回溯
@@ -142,7 +142,7 @@ if (cur->right) {
那么本题整体代码如下:
-```C++
+```CPP
class Solution {
private:
@@ -183,7 +183,7 @@ public:
那么如上代码可以精简成如下代码:
-```C++
+```CPP
class Solution {
private:
@@ -215,8 +215,52 @@ public:
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。
-**如果这里还不理解的话,可以看这篇[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA),我这这篇中详细的解释了递归中如何隐藏着回溯。 **
+为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
+```CPP
+if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
+```
+
+改成如下代码:
+
+```CPP
+path += "->";
+traversal(cur->left, path, result); // 左
+```
+
+即:
+
+```CPP
+if (cur->left) {
+ path += "->";
+ traversal(cur->left, path, result); // 左
+}
+if (cur->right) {
+ path += "->";
+ traversal(cur->right, path, result); // 右
+}
+```
+
+此时就没有回溯了,这个代码就是通过不了的了。
+
+如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。
+
+```CPP
+if (cur->left) {
+ path += "->";
+ traversal(cur->left, path, result); // 左
+ path.pop_back(); // 回溯
+ path.pop_back();
+}
+if (cur->right) {
+ path += "->";
+ traversal(cur->right, path, result); // 右
+ path.pop_back(); // 回溯
+ path.pop_back();
+}
+```
+
+**大家应该可以感受出来,如果把 `path + "->"`作为函数参数就是可以的,因为并有没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)**
**综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现了出来了。**
@@ -225,13 +269,14 @@ public:
## 迭代法
-至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)。
+
+至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)和[二叉树:前中后序迭代方式统一写法](https://programmercarl.com/二叉树的统一迭代法.html)。
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。
C++代码如下:
-```C++
+```CPP
class Solution {
public:
vector binaryTreePaths(TreeNode* root) {
@@ -262,7 +307,7 @@ public:
```
当然,使用java的同学,可以直接定义一个成员变量为object的栈`Stack
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 动态规划:Carl称它为排列总和!
## 377. 组合总和 Ⅳ
-题目链接:https://leetcode-cn.com/problems/combination-sum-iv/
+[力扣题目链接](https://leetcode-cn.com/problems/combination-sum-iv/)
难度:中等
@@ -35,6 +36,8 @@ target = 4
## 思路
+对完全背包还不了解的同学,可以看这篇:[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)
+
本题题目描述说是求组合,但又说是可以元素相同顺序不同的组合算两个组合,**其实就是求排列!**
弄清什么是组合,什么是排列很重要。
@@ -43,7 +46,7 @@ target = 4
排列强调顺序,(1,5)和(5,1)是两个不同的排列。
-大家在公众号里学习回溯算法专题的时候,一定做过这两道题目[回溯算法:39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)和[回溯算法:40.组合总和II](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)会感觉这两题和本题很像!
+大家在公众号里学习回溯算法专题的时候,一定做过这两道题目[回溯算法:39.组合总和](https://programmercarl.com/0039.组合总和.html)和[回溯算法:40.组合总和II](https://programmercarl.com/0040.组合总和II.html)会感觉这两题和本题很像!
但其本质是本题求的是排列总和,而且仅仅是求排列总和的个数,并不是把所有的排列都列出来。
@@ -61,7 +64,7 @@ dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导
因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。
-在[动态规划:494.目标和](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw) 和 [动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)中我们已经讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];
+在[动态规划:494.目标和](https://programmercarl.com/0494.目标和.html) 和 [动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)中我们已经讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];
本题也一样。
@@ -87,7 +90,7 @@ dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导
本题要求的是排列,那么这个for循环嵌套的顺序可以有说法了。
-在[动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ) 中就已经讲过了。
+在[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) 中就已经讲过了。
**如果求组合数就是外层for循环遍历物品,内层for遍历背包**。
@@ -107,7 +110,7 @@ dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int combinationSum4(vector& nums, int target) {
@@ -134,7 +137,7 @@ C++测试用例有超过两个树相加超过int的数据,所以需要在if里
**求装满背包有几种方法,递归公式都是一样的,没有什么差别,但关键在于遍历顺序!**
-本题与[动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)就是一个鲜明的对比,一个是求排列,一个是求组合,遍历顺序完全不同。
+本题与[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)就是一个鲜明的对比,一个是求排列,一个是求组合,遍历顺序完全不同。
如果对遍历顺序没有深度理解的话,做这种完全背包的题目会很懵逼,即使题目刷过了可能也不太清楚具体是怎么过的。
@@ -220,10 +223,27 @@ const combinationSum4 = (nums, target) => {
};
```
+Rust
+```Rust
+impl Solution {
+ pub fn combination_sum4(nums: Vec, target: i32) -> i32 {
+ let mut dp = vec![0; target as usize + 1];
+ dp[0] = 1;
+ for i in 1..=target as usize {
+ for &j in nums.iter() {
+ if i as i32 >= j {
+ dp[i] += dp[i- j as usize];
+ }
+ }
+ }
+ return dp[target as usize];
+ }
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0383.赎金信.md b/problems/0383.赎金信.md
index a5315b0e..75b31698 100644
--- a/problems/0383.赎金信.md
+++ b/problems/0383.赎金信.md
@@ -12,7 +12,7 @@
# 383. 赎金信
-https://leetcode-cn.com/problems/ransom-note/
+[力扣题目链接](https://leetcode-cn.com/problems/ransom-note/)
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
@@ -28,7 +28,7 @@ canConstruct("aa", "aab") -> true
## 思路
-这道题目和[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)很像,[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)相当于求 字符串a 和 字符串b 是否可以相互组成 ,而这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。
+这道题目和[242.有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html)很像,[242.有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html)相当于求 字符串a 和 字符串b 是否可以相互组成 ,而这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。
本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。
@@ -40,7 +40,7 @@ canConstruct("aa", "aab") -> true
那么第一个思路其实就是暴力枚举了,两层for循环,不断去寻找,代码如下:
-```C++
+```CPP
// 时间复杂度: O(n^2)
// 空间复杂度:O(1)
class Solution {
@@ -79,7 +79,7 @@ public:
代码如下:
-```C++
+```CPP
// 时间复杂度: O(n)
// 空间复杂度:O(1)
class Solution {
@@ -209,6 +209,22 @@ class Solution(object):
return True
```
+Python写法四:
+
+```python3
+class Solution:
+ def canConstruct(self, ransomNote: str, magazine: str) -> bool:
+ c1 = collections.Counter(ransomNote)
+ c2 = collections.Counter(magazine)
+ x = c1 - c2
+ #x只保留值大于0的符号,当c1里面的符号个数小于c2时,不会被保留
+ #所以x只保留下了,magazine不能表达的
+ if(len(x)==0):
+ return True
+ else:
+ return False
+```
+
Go:
```go
@@ -251,9 +267,79 @@ var canConstruct = function(ransomNote, magazine) {
```
+PHP:
+```php
+class Solution {
+ /**
+ * @param String $ransomNote
+ * @param String $magazine
+ * @return Boolean
+ */
+ function canConstruct($ransomNote, $magazine) {
+ if (count($ransomNote) > count($magazine)) {
+ return false;
+ }
+ $map = [];
+ for ($i = 0; $i < strlen($magazine); $i++) {
+ $map[$magazine[$i]] = ($map[$magazine[$i]] ?? 0) + 1;
+ }
+ for ($i = 0; $i < strlen($ransomNote); $i++) {
+ if (!isset($map[$ransomNote[$i]]) || --$map[$ransomNote[$i]] < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+```
+
+Swift:
+```swift
+func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool {
+ var record = Array(repeating: 0, count: 26);
+ let aUnicodeScalarValue = "a".unicodeScalars.first!.value
+ for unicodeScalar in magazine.unicodeScalars {
+ // 通过record 记录 magazine 里各个字符出现的次数
+ let idx: Int = Int(unicodeScalar.value - aUnicodeScalarValue)
+ record[idx] += 1
+ }
+ for unicodeScalar in ransomNote.unicodeScalars {
+ // 遍历 ransomNote,在record里对应的字符个数做 -- 操作
+ let idx: Int = Int(unicodeScalar.value - aUnicodeScalarValue)
+ record[idx] -= 1
+ // 如果小于零说明在magazine没有
+ if record[idx] < 0 {
+ return false
+ }
+ }
+ return true
+}
+```
+
+Rust:
+```rust
+impl Solution {
+ pub fn can_construct(ransom_note: String, magazine: String) -> bool {
+ let baseChar = 'a';
+ let mut record = vec![0; 26];
+
+ for byte in magazine.bytes() {
+ record[byte as usize - baseChar as usize] += 1;
+ }
+
+ for byte in ransom_note.bytes() {
+ record[byte as usize - baseChar as usize] -= 1;
+ if record[byte as usize - baseChar as usize] < 0 {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0392.判断子序列.md b/problems/0392.判断子序列.md
index d97d2684..784e3bbc 100644
--- a/problems/0392.判断子序列.md
+++ b/problems/0392.判断子序列.md
@@ -9,7 +9,7 @@
## 392.判断子序列
-题目链接:https://leetcode-cn.com/problems/is-subsequence/
+[力扣题目链接](https://leetcode-cn.com/problems/is-subsequence/)
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
@@ -107,7 +107,7 @@ dp[i][j]表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字
动规五部曲分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool isSubsequence(string s, string t) {
@@ -180,7 +180,49 @@ class Solution:
return False
```
+JavaScript:
+
+```javascript
+const isSubsequence = (s, t) => {
+ // s、t的长度
+ const [m, n] = [s.length, t.length];
+ // dp全初始化为0
+ const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0));
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ // 更新dp[i][j],两种情况
+ if (s[i - 1] === t[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = dp[i][j - 1];
+ }
+ }
+ }
+ // 遍历结束,判断dp右下角的数是否等于s的长度
+ return dp[m][n] === m ? true : false;
+};
+```
+
Go:
+```go
+func isSubsequence(s string, t string) bool {
+ dp := make([][]int,len(s)+1)
+ for i:=0;i
+
diff --git a/problems/0404.左叶子之和.md b/problems/0404.左叶子之和.md
index aa758367..c0eb7c8e 100644
--- a/problems/0404.左叶子之和.md
+++ b/problems/0404.左叶子之和.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 404.左叶子之和
+# 404.左叶子之和
-题目地址:https://leetcode-cn.com/problems/sum-of-left-leaves/
+[力扣题目链接](https://leetcode-cn.com/problems/sum-of-left-leaves/)
计算给定二叉树的所有左叶子之和。
@@ -17,7 +17,7 @@

-## 思路
+# 思路
**首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。**
@@ -65,7 +65,7 @@ if (root == NULL) return 0;
代码如下:
-```C++
+```CPP
int leftValue = sumOfLeftLeaves(root->left); // 左
int rightValue = sumOfLeftLeaves(root->right); // 右
// 中
@@ -81,7 +81,7 @@ return sum;
整体递归代码如下:
-```C++
+```CPP
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
@@ -102,7 +102,7 @@ public:
以上代码精简之后如下:
-```C++
+```CPP
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
@@ -119,11 +119,11 @@ public:
## 迭代法
-本题迭代法使用前中后序都是可以的,只要把左叶子节点统计出来,就可以了,那么参考文章 [二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)中的写法,可以写出一个前序遍历的迭代法。
+本题迭代法使用前中后序都是可以的,只要把左叶子节点统计出来,就可以了,那么参考文章 [二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)和[二叉树:迭代法统一写法](https://programmercarl.com/二叉树的统一迭代法.html)中的写法,可以写出一个前序遍历的迭代法。
判断条件都是一样的,代码如下:
-```C++
+```CPP
class Solution {
public:
@@ -146,7 +146,7 @@ public:
};
```
-## 总结
+# 总结
这道题目要求左叶子之和,其实是比较绕的,因为不能判断本节点是不是左叶子节点。
@@ -157,9 +157,9 @@ public:
希望通过这道题目,可以扩展大家对二叉树的解题思路。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
**递归**
@@ -201,42 +201,84 @@ class Solution {
}
}
```
+```java
+// 层序遍历迭代法
+class Solution {
+ public int sumOfLeftLeaves(TreeNode root) {
+ int sum = 0;
+ if (root == null) return 0;
+ Queue queue = new LinkedList<>();
+ queue.offer(root);
+ while (!queue.isEmpty()) {
+ int size = queue.size();
+ while (size -- > 0) {
+ TreeNode node = queue.poll();
+ if (node.left != null) { // 左节点不为空
+ queue.offer(node.left);
+ if (node.left.left == null && node.left.right == null){ // 左叶子节点
+ sum += node.left.val;
+ }
+ }
+ if (node.right != null) queue.offer(node.right);
+ }
+ }
+ return sum;
+ }
+}
+```
+## Python
-Python:
-```Python
**递归**
-# 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
+```python
class Solution:
def sumOfLeftLeaves(self, root: TreeNode) -> int:
- self.res=0
- def areleftleaves(root):
- if not root:return
- if root.left and (not root.left.left) and (not root.left.right):self.res+=root.left.val
- areleftleaves(root.left)
- areleftleaves(root.right)
- areleftleaves(root)
- return self.res
+ if not root:
+ return 0
+
+ left_left_leaves_sum = self.sumOfLeftLeaves(root.left) # 左
+ right_left_leaves_sum = self.sumOfLeftLeaves(root.right) # 右
+
+ cur_left_leaf_val = 0
+ if root.left and not root.left.left and not root.left.right:
+ cur_left_leaf_val = root.left.val # 中
+
+ return cur_left_leaf_val + left_left_leaves_sum + right_left_leaves_sum
```
-Go:
-> 递归法
+**迭代**
+```python
+class Solution:
+ def sumOfLeftLeaves(self, root: TreeNode) -> int:
+ """
+ Idea: Each time check current node's left node.
+ If current node don't have one, skip it.
+ """
+ stack = []
+ if root:
+ stack.append(root)
+ res = 0
+
+ while stack:
+ # 每次都把当前节点的左节点加进去.
+ cur_node = stack.pop()
+ if cur_node.left and not cur_node.left.left and not cur_node.left.right:
+ res += cur_node.left.val
+
+ if cur_node.left:
+ stack.append(cur_node.left)
+ if cur_node.right:
+ stack.append(cur_node.right)
+
+ return res
+```
+
+## Go
+
+**递归法**
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
func sumOfLeftLeaves(root *TreeNode) int {
var res int
findLeft(root,&res)
@@ -256,17 +298,9 @@ func findLeft(root *TreeNode,res *int){
}
```
-> 迭代法
+**迭代法**
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
func sumOfLeftLeaves(root *TreeNode) int {
var res int
queue:=list.New()
@@ -291,8 +325,10 @@ func sumOfLeftLeaves(root *TreeNode) int {
```
-JavaScript:
-递归版本
+## JavaScript
+
+**递归法**
+
```javascript
var sumOfLeftLeaves = function(root) {
//采用后序遍历 递归遍历
@@ -315,7 +351,8 @@ var sumOfLeftLeaves = function(root) {
return nodesSum(root);
};
```
-迭代版本
+
+**迭代法**
```javascript
var sumOfLeftLeaves = function(root) {
//采用层序遍历
@@ -345,4 +382,4 @@ var sumOfLeftLeaves = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0406.根据身高重建队列.md b/problems/0406.根据身高重建队列.md
index a5f66a5d..bd27b1b2 100644
--- a/problems/0406.根据身高重建队列.md
+++ b/problems/0406.根据身高重建队列.md
@@ -9,7 +9,7 @@
## 406.根据身高重建队列
-题目链接:https://leetcode-cn.com/problems/queue-reconstruction-by-height/
+[力扣题目链接](https://leetcode-cn.com/problems/queue-reconstruction-by-height/)
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
@@ -43,9 +43,9 @@
本题有两个维度,h和k,看到这种题目一定要想如何确定一个维度,然后在按照另一个维度重新排列。
-其实如果大家认真做了[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ),就会发现和此题有点点的像。
+其实如果大家认真做了[135. 分发糖果](https://programmercarl.com/0135.分发糖果.html),就会发现和此题有点点的像。
-在[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)我就强调过一次,遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。
+在[135. 分发糖果](https://programmercarl.com/0135.分发糖果.html)我就强调过一次,遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。
**如果两个维度一起考虑一定会顾此失彼**。
@@ -76,11 +76,11 @@
一些同学可能也会疑惑,你怎么知道局部最优就可以推出全局最优呢? 有数学证明么?
-在贪心系列开篇词[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/O935TaoHE9Eexwe_vSbRAg)中,我已经讲过了这个问题了。
+在贪心系列开篇词[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html)中,我已经讲过了这个问题了。
刷题或者面试的时候,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心,至于严格的数学证明,就不在讨论范围内了。
-如果没有读过[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/O935TaoHE9Eexwe_vSbRAg)的同学建议读一下,相信对贪心就有初步的了解了。
+如果没有读过[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html)的同学建议读一下,相信对贪心就有初步的了解了。
回归本题,整个插入过程如下:
@@ -99,7 +99,7 @@
C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -127,7 +127,7 @@ public:
改成链表之后,C++代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -157,15 +157,15 @@ public:
大家可以把两个版本的代码提交一下试试,就可以发现其差别了!
-关于本题使用数组还是使用链表的性能差异,我在[贪心算法:根据身高重建队列(续集)](https://mp.weixin.qq.com/s/K-pRN0lzR-iZhoi-1FgbSQ)中详细讲解了一波
+关于本题使用数组还是使用链表的性能差异,我在[贪心算法:根据身高重建队列(续集)](https://programmercarl.com/根据身高重建队列(vector原理讲解).html)中详细讲解了一波
## 总结
-关于出现两个维度一起考虑的情况,我们已经做过两道题目了,另一道就是[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)。
+关于出现两个维度一起考虑的情况,我们已经做过两道题目了,另一道就是[135. 分发糖果](https://programmercarl.com/0135.分发糖果.html)。
**其技巧都是确定一边然后贪心另一边,两边一起考虑,就会顾此失彼**。
-这道题目可以说比[135. 分发糖果](https://mp.weixin.qq.com/s/8MwlgFfvaNYmjGwjuMlETQ)难不少,其贪心的策略也是比较巧妙。
+这道题目可以说比[135. 分发糖果](https://programmercarl.com/0135.分发糖果.html)难不少,其贪心的策略也是比较巧妙。
最后我给出了两个版本的代码,可以明显看是使用C++中的list(底层链表实现)比vector(数组)效率高得多。
@@ -209,15 +209,38 @@ Python:
```python
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
+ # 先按照h维度的身高顺序从高到低排序。确定第一个维度
+ # lambda返回的是一个元组:当-x[0](维度h)相同时,再根据x[1](维度k)从小到大排序
people.sort(key=lambda x: (-x[0], x[1]))
que = []
+
+ # 根据每个元素的第二个维度k,贪心算法,进行插入
+ # people已经排序过了:同一高度时k值小的排前面。
for p in people:
que.insert(p[1], p)
return que
```
Go:
-
+```golang
+func reconstructQueue(people [][]int) [][]int {
+ //先将身高从大到小排序,确定最大个子的相对位置
+ sort.Slice(people,func(i,j int)bool{
+ if people[i][0]==people[j][0]{
+ return people[i][1]people[j][0]//这个只是确保身高按照由大到小的顺序来排,并不确定K是按照从小到大排序的
+ })
+ //再按照K进行插入排序,优先插入K小的
+ result := make([][]int, 0)
+ for _, info := range people {
+ result = append(result, info)
+ copy(result[info[1] +1:], result[info[1]:])//将插入位置之后的元素后移动一位(意思是腾出空间)
+ result[info[1]] = info//将插入元素位置插入元素
+ }
+ return result
+}
+```
Javascript:
```Javascript
var reconstructQueue = function(people) {
@@ -243,4 +266,4 @@ var reconstructQueue = function(people) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md
index 75c665cd..415ff88b 100644
--- a/problems/0416.分割等和子集.md
+++ b/problems/0416.分割等和子集.md
@@ -8,7 +8,7 @@
## 416. 分割等和子集
-题目链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/
+[力扣题目链接](https://leetcode-cn.com/problems/partition-equal-subset-sum/)
题目难易:中等
@@ -22,7 +22,7 @@
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
-
+
示例 2:
输入: [1, 2, 3, 5]
输出: false
@@ -47,8 +47,8 @@
如果对01背包不够了解,建议仔细看完如下两篇:
-* [动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)
-* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)
+* [动态规划:关于01背包问题,你该了解这些!](https://programmercarl.com/背包理论基础01背包-1.html)
+* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)
## 01背包问题
@@ -106,7 +106,7 @@
代码如下:
-```C++
+```CPP
// 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
// 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
vector dp(10001, 0);
@@ -114,11 +114,11 @@ vector dp(10001, 0);
4. 确定遍历顺序
-在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒叙遍历!
+在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒叙遍历!
代码如下:
-```C++
+```CPP
// 开始 01背包
for(int i = 0; i < nums.size(); i++) {
for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
@@ -141,7 +141,7 @@ dp[i]的数值一定是小于等于i的。
综上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool canPartition(vector& nums) {
@@ -191,35 +191,23 @@ Java:
```Java
class Solution {
public boolean canPartition(int[] nums) {
+ if(nums == null || nums.length == 0) return false;
+ int n = nums.length;
int sum = 0;
- for (int i : nums) {
- sum += i;
+ for(int num : nums){
+ sum += num;
}
- if ((sum & 1) == 1) {
- return false;
- }
- int length = nums.length;
- int target = sum >> 1;
- //dp[j]表示前i个元素可以找到相加等于j情况
- boolean[] dp = new boolean[target + 1];
- //对于第一个元素,只有当j=nums[0]时,才恰好填充满
- if (nums[0] <= target) {
- dp[nums[0]] = true;
- }
-
- for (int i = 1; i < length; i++) {
- //j由右往左直到nums[i]
- for (int j = target; j >= nums[i]; j--) {
- //只有两种情况,要么放,要么不放
- //取其中的TRUE值
- dp[j] = dp[j] || dp[j - nums[i]];
- }
- //一旦满足,结束,因为只需要找到一组值即可
- if (dp[target]) {
- return dp[target];
+ //总和为奇数,不能平分
+ if(sum % 2 != 0) return false;
+ int target = sum / 2;
+ int[] dp = new int[target + 1];
+ for(int i = 0; i < n; i++){
+ for(int j = target; j >= nums[i]; j--){
+ //物品 i 的重量是 nums[i],其价值也是 nums[i]
+ dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);
}
}
- return dp[target];
+ return dp[target] == target;
}
}
```
@@ -239,6 +227,45 @@ class Solution:
```
Go:
+```
+func canPartition(nums []int) bool {
+ /**
+ 动态五部曲:
+ 1.确定dp数组和下标含义
+ 2.确定递推公式
+ 3.dp数组初始化
+ 4.dp遍历顺序
+ 5.打印
+ **/
+ //确定和
+ var sum int
+ for _,v:=range nums{
+ sum+=v
+ }
+ if sum%2!=0{ //如果和为奇数,则不可能分成两个相等的数组
+ return false
+ }
+ sum/=2
+ //确定dp数组和下标含义
+ var dp [][]bool //dp[i][j] 表示: 前i个石头是否总和不大于J
+ //初始化数组
+ dp=make([][]bool,len(nums)+1)
+ for i,_:=range dp{
+ dp[i]=make([]bool,sum+1)
+ dp[i][0]=true
+ }
+ for i:=1;i<=len(nums);i++{
+ for j:=1;j<=sum;j++{//j是固定总量
+ if j>=nums[i-1]{//如果容量够用则可放入背包
+ dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i-1]]
+ }else{//如果容量不够用则不拿,维持前一个状态
+ dp[i][j]=dp[i-1][j]
+ }
+ }
+ }
+ return dp[len(nums)][sum]
+}
+```
javaScript:
@@ -266,4 +293,4 @@ var canPartition = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0435.无重叠区间.md b/problems/0435.无重叠区间.md
index d32c2ebb..4e850114 100644
--- a/problems/0435.无重叠区间.md
+++ b/problems/0435.无重叠区间.md
@@ -9,7 +9,7 @@
## 435. 无重叠区间
-题目链接:https://leetcode-cn.com/problems/non-overlapping-intervals/
+[力扣题目链接](https://leetcode-cn.com/problems/non-overlapping-intervals/)
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
@@ -122,11 +122,11 @@ public:
## 补充
-本题其实和[452.用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
+本题其实和[452.用最少数量的箭引爆气球](https://programmercarl.com/0452.用最少数量的箭引爆气球.html)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
-把[452.用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)代码稍做修改,就可以AC本题。
+把[452.用最少数量的箭引爆气球](https://programmercarl.com/0452.用最少数量的箭引爆气球.html)代码稍做修改,就可以AC本题。
-```C++
+```CPP
class Solution {
public:
// 按照区间右边界排序
@@ -152,7 +152,7 @@ public:
```
这里按照 左区间遍历,或者按照右边界遍历,都可以AC,具体原因我还没有仔细看,后面有空再补充。
-```C++
+```CPP
class Solution {
public:
// 按照区间左边界排序
@@ -186,27 +186,27 @@ Java:
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length < 2) return 0;
+
Arrays.sort(intervals, new Comparator() {
@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;
}
}
```
@@ -250,8 +250,31 @@ class Solution:
```
Go:
-
+```golang
+func eraseOverlapIntervals(intervals [][]int) int {
+ var flag int
+ //先排序
+ sort.Slice(intervals,func(i,j int)bool{
+ return intervals[i][0]intervals[i][0]{
+ flag++
+ intervals[i][1]=min(intervals[i-1][1],intervals[i][1])//由于是先排序的,所以,第一位是递增顺序,故只需要将临近两个元素的第二个值最小值更新到该元素的第二个值即可作之后的判断
+ }
+ }
+ return flag
+}
+func min(a,b int)int{
+ if a>b{
+ return b
+ }
+ return a
+}
+```
Javascript:
+- 按右边界排序
```Javascript
var eraseOverlapIntervals = function(intervals) {
intervals.sort((a, b) => {
@@ -263,7 +286,7 @@ var eraseOverlapIntervals = function(intervals) {
for(let i = 1; i < intervals.length; i++) {
let interval = intervals[i]
- if(interval[0] >= right) {
+ if(interval[0] >= end) {
end = interval[1]
count += 1
}
@@ -272,10 +295,28 @@ var eraseOverlapIntervals = function(intervals) {
return intervals.length - count
};
```
+- 按左边界排序
+```js
+var eraseOverlapIntervals = function(intervals) {
+ // 按照左边界升序排列
+ intervals.sort((a, b) => a[0] - b[0])
+ let count = 1
+ let end = intervals[intervals.length - 1][0]
+ // 倒序遍历,对单个区间来说,左边界越大越好,因为给前面区间的空间越大
+ for(let i = intervals.length - 2; i >= 0; i--) {
+ if(intervals[i][1] <= end) {
+ count++
+ end = intervals[i][0]
+ }
+ }
+ // count 记录的是最大非重复区间的个数
+ return intervals.length - count
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0450.删除二叉搜索树中的节点.md b/problems/0450.删除二叉搜索树中的节点.md
index 4695ed50..b12d40aa 100644
--- a/problems/0450.删除二叉搜索树中的节点.md
+++ b/problems/0450.删除二叉搜索树中的节点.md
@@ -9,9 +9,9 @@
> 二叉搜索树删除节点就涉及到结构调整了
-## 450.删除二叉搜索树中的节点
+# 450.删除二叉搜索树中的节点
-题目链接: https://leetcode-cn.com/problems/delete-node-in-a-bst/
+[力扣题目链接]( https://leetcode-cn.com/problems/delete-node-in-a-bst/)
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
@@ -25,7 +25,7 @@

-## 思路
+# 思路
搜索树的节点删除要比节点增加复杂的多,有很多情况需要考虑,做好心里准备。
@@ -35,7 +35,7 @@
* 确定递归函数参数以及返回值
-说道递归函数的返回值,在[二叉树:搜索树中的插入操作](https://mp.weixin.qq.com/s/lwKkLQcfbCNX2W-5SOeZEA)中通过递归返回值来加入新节点, 这里也可以通过递归返回值删除节点。
+说道递归函数的返回值,在[二叉树:搜索树中的插入操作](https://programmercarl.com/0701.二叉搜索树中的插入操作.html)中通过递归返回值来加入新节点, 这里也可以通过递归返回值删除节点。
代码如下:
@@ -78,7 +78,7 @@ if (root == nullptr) return root;
代码如下:
-```C++
+```CPP
if (root->val == key) {
// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点
@@ -111,17 +111,32 @@ return root;
**整体代码如下:(注释中:情况1,2,3,4,5和上面分析严格对应)**
-```C++
+```CPP
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr) return root; // 第一种情况:没找到删除的节点,遍历到空节点直接返回了
if (root->val == key) {
// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
+ if (root->left == nullptr && root->right == nullptr) {
+ ///! 内存释放
+ delete root;
+ return nullptr;
+ }
// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点
- if (root->left == nullptr) return root->right;
+ else if (root->left == nullptr) {
+ auto retNode = root->right;
+ ///! 内存释放
+ delete root;
+ return retNode;
+ }
// 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
- else if (root->right == nullptr) return root->left;
+ else if (root->right == nullptr) {
+ auto retNode = root->left;
+ ///! 内存释放
+ delete root;
+ return retNode;
+ }
// 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置
// 并返回删除节点右孩子为新的根节点。
else {
@@ -156,7 +171,7 @@ public:
代码如下:(关键部分已经注释)
-```C++
+```CPP
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
@@ -186,7 +201,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
private:
// 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上
@@ -228,7 +243,7 @@ public:
};
```
-## 总结
+# 总结
读完本篇,大家会发现二叉搜索树删除节点比增加节点复杂的多。
@@ -246,10 +261,10 @@ public:
迭代法其实不太容易写出来,所以如果是初学者的话,彻底掌握第一种递归写法就够了。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```java
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
@@ -278,15 +293,36 @@ class Solution {
}
}
```
+```java
+// 解法2
+class Solution {
+ public TreeNode deleteNode(TreeNode root, int key) {
+ if (root == null) return root;
+ if (root.val == key) {
+ if (root.left == null) {
+ return root.right;
+ } else if (root.right == null) {
+ return root.left;
+ } else {
+ TreeNode cur = root.right;
+ while (cur.left != null) {
+ cur = cur.left;
+ }
+ cur.left = root.left;
+ root = root.right;
+ return root;
+ }
+ }
+ if (root.val > key) root.left = deleteNode(root.left, key);
+ if (root.val < key) root.right = deleteNode(root.right, key);
+ return root;
+ }
+}
+```
+
+## Python
-Python:
```python
-# 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 deleteNode(self, root: TreeNode, key: int) -> TreeNode:
if not root: return root #第一种情况:没找到删除的节点,遍历到空节点直接返回了
@@ -318,8 +354,9 @@ class Solution:
return root
```
-Go:
+## Go
```Go
+// 递归版本
func deleteNode(root *TreeNode, key int) *TreeNode {
if root==nil{
return nil
@@ -356,11 +393,56 @@ func deleteNode1(root *TreeNode)*TreeNode{
root.Left=deleteNode1(root.Left)
return root
}
+// 迭代版本
+func deleteOneNode(target *TreeNode) *TreeNode {
+ if target == nil {
+ return target
+ }
+ if target.Right == nil {
+ return target.Left
+ }
+ cur := target.Right
+ for cur.Left != nil {
+ cur = cur.Left
+ }
+ cur.Left = target.Left
+ return target.Right
+}
+func deleteNode(root *TreeNode, key int) *TreeNode {
+ // 特殊情况处理
+ if root == nil {
+ return root
+ }
+ cur := root
+ var pre *TreeNode
+ for cur != nil {
+ if cur.Val == key {
+ break
+ }
+ pre = cur
+ if cur.Val > key {
+ cur = cur.Left
+ } else {
+ cur = cur.Right
+ }
+ }
+ if pre == nil {
+ return deleteOneNode(cur)
+ }
+ // pre 要知道是删除左孩子还有右孩子
+ if pre.Left != nil && pre.Left.Val == key {
+ pre.Left = deleteOneNode(cur)
+ }
+ if pre.Right != nil && pre.Right.Val == key {
+ pre.Right = deleteOneNode(cur)
+ }
+ return root
+}
```
-JavaScript版本
+## JavaScript
-> 递归
+递归
```javascript
/**
@@ -410,4 +492,4 @@ var deleteNode = function (root, key) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0452.用最少数量的箭引爆气球.md b/problems/0452.用最少数量的箭引爆气球.md
index bb3ebbdc..07141558 100644
--- a/problems/0452.用最少数量的箭引爆气球.md
+++ b/problems/0452.用最少数量的箭引爆气球.md
@@ -9,7 +9,7 @@
## 452. 用最少数量的箭引爆气球
-题目链接:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/
+[力扣题目链接](https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/)
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。
@@ -84,7 +84,7 @@
C++代码如下:
-```C++
+```CPP
class Solution {
private:
static bool cmp(const vector& a, const vector& b) {
@@ -175,6 +175,30 @@ class Solution:
Go:
+```golang
+func findMinArrowShots(points [][]int) int {
+ var res int =1//弓箭数
+ //先按照第一位排序
+ sort.Slice(points,func (i,j int) bool{
+ return points[i][0]b{
+ return b
+ }
+ return a
+}
+```
Javascript:
```Javascript
var findMinArrowShots = function(points) {
@@ -194,9 +218,33 @@ var findMinArrowShots = function(points) {
};
```
+C:
+```c
+int cmp(const void *a,const void *b)
+{
+ return ((*((int**)a))[0] > (*((int**)b))[0]);
+}
+
+int findMinArrowShots(int** points, int pointsSize, int* pointsColSize){
+ //将points数组作升序排序
+ qsort(points, pointsSize, sizeof(points[0]),cmp);
+
+ int arrowNum = 1;
+ int i = 1;
+ for(i = 1; i < pointsSize; i++) {
+ //若前一个气球与当前气球不重叠,证明需要增加箭的数量
+ if(points[i][0] > points[i-1][1])
+ arrowNum++;
+ else
+ //若前一个气球与当前气球重叠,判断并更新最小的x_end
+ points[i][1] = points[i][1] > points[i-1][1] ? points[i-1][1] : points[i][1];
+ }
+ return arrowNum;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0454.四数相加II.md b/problems/0454.四数相加II.md
index 0621ab5b..475f93a0 100644
--- a/problems/0454.四数相加II.md
+++ b/problems/0454.四数相加II.md
@@ -11,8 +11,7 @@
# 第454题.四数相加II
-
-https://leetcode-cn.com/problems/4sum-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/4sum-ii/)
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
@@ -35,9 +34,9 @@ D = [ 0, 2]
# 思路
-本题咋眼一看好像和[0015.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A),[0018.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)差不多,其实差很多。
+本题咋眼一看好像和[0015.三数之和](https://programmercarl.com/0015.三数之和.html),[0018.四数之和](https://programmercarl.com/0018.四数之和.html)差不多,其实差很多。
-**本题是使用哈希法的经典题目,而[0015.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A),[0018.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)并不合适使用哈希法**,因为三数之和和四数之和这两道题目使用哈希法在不超时的情况下做到对结果去重是很困难的,很有多细节需要处理。
+**本题是使用哈希法的经典题目,而[0015.三数之和](https://programmercarl.com/0015.三数之和.html),[0018.四数之和](https://programmercarl.com/0018.四数之和.html)并不合适使用哈希法**,因为三数之和和四数之和这两道题目使用哈希法在不超时的情况下做到对结果去重是很困难的,很有多细节需要处理。
**而这道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!**
@@ -53,7 +52,7 @@ D = [ 0, 2]
C++代码:
-```C++
+```CPP
class Solution {
public:
int fourSumCount(vector& A, vector& B, vector& C, vector& D) {
@@ -162,15 +161,9 @@ class Solution(object):
# count=0
# for x3 in nums3:
# for x4 in nums4:
-# key = -x3-x4
-# value = hashmap.get(key)
-
- # dict的get方法会返回None(key不存在)或者key对应的value
- # 所以如果value==0,就会继续执行or,count+0,否则就会直接加value
- # 这样就不用去写if判断了
-
-# count += value or 0
-
+# key = 0 - x3 - x4
+# value = hashmap[key] # 若差值(key)不存在,则value被赋值0
+# count += value
# return count
```
@@ -228,10 +221,95 @@ var fourSumCount = function(nums1, nums2, nums3, nums4) {
```
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer[] $nums1
+ * @param Integer[] $nums2
+ * @param Integer[] $nums3
+ * @param Integer[] $nums4
+ * @return Integer
+ */
+ function fourSumCount($nums1, $nums2, $nums3, $nums4) {
+ $map = [];
+ foreach ($nums1 as $n1) {
+ foreach ($nums2 as $n2) {
+ $temp = $n1 + $n2;
+ $map[$temp] = isset($map[$temp]) ? $map[$temp]+1 : 1;
+ }
+ }
+ $count = 0;
+ foreach ($nums3 as $n3) {
+ foreach ($nums4 as $n4) {
+ $temp = 0 - $n3 - $n4;
+ if (isset($map[$temp])) {
+ $count += $map[$temp];
+ }
+ }
+ }
+ return $count;
+ }
+}
+```
+Swift:
+```swift
+func fourSumCount(_ nums1: [Int], _ nums2: [Int], _ nums3: [Int], _ nums4: [Int]) -> Int {
+ // key:a+b的数值,value:a+b数值出现的次数
+ var map = [Int: Int]()
+ // 遍历nums1和nums2数组,统计两个数组元素之和,和出现的次数,放到map中
+ for i in 0 ..< nums1.count {
+ for j in 0 ..< nums2.count {
+ let sum1 = nums1[i] + nums2[j]
+ map[sum1] = (map[sum1] ?? 0) + 1
+ }
+ }
+ // 统计a+b+c+d = 0 出现的次数
+ var res = 0
+ // 在遍历大num3和num4数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
+ for i in 0 ..< nums3.count {
+ for j in 0 ..< nums4.count {
+ let sum2 = nums3[i] + nums4[j]
+ let other = 0 - sum2
+ if map.keys.contains(other) {
+ res += map[other]!
+ }
+ }
+ }
+ return res
+}
+```
+
+Rust:
+```rust
+use std::collections::HashMap;
+impl Solution {
+ pub fn four_sum_count(nums1: Vec, nums2: Vec, nums3: Vec, nums4: Vec) -> i32 {
+ let mut umap:HashMap = HashMap::new();
+ for num1 in &nums1 {
+ for num2 in &nums2 {
+ *umap.entry(num1 + num2).or_insert(0) += 1;
+ }
+ }
+
+ let mut count = 0;
+
+ for num3 in &nums3 {
+ for num4 in &nums4 {
+ let target:i32 = - (num3 + num4);
+ count += umap.get(&target).unwrap_or(&0);
+ }
+ }
+
+ count
+ }
+}
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0455.分发饼干.md b/problems/0455.分发饼干.md
index 6b121e36..8e20c402 100644
--- a/problems/0455.分发饼干.md
+++ b/problems/0455.分发饼干.md
@@ -9,7 +9,7 @@
## 455.分发饼干
-题目链接:https://leetcode-cn.com/problems/assign-cookies/
+[力扣题目链接](https://leetcode-cn.com/problems/assign-cookies/)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
@@ -59,7 +59,7 @@
C++代码整体如下:
-```C++
+```CPP
// 时间复杂度:O(nlogn)
// 空间复杂度:O(1)
class Solution {
@@ -88,7 +88,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
int findContentChildren(vector& g, vector& s) {
@@ -117,6 +117,7 @@ public:
Java:
```java
class Solution {
+ // 思路1:优先考虑饼干,小饼干先喂饱小胃口
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
@@ -132,10 +133,30 @@ class Solution {
}
}
```
+```java
+class Solution {
+ // 思路2:优先考虑胃口,先喂饱大胃口
+ public int findContentChildren(int[] g, int[] s) {
+ Arrays.sort(g);
+ Arrays.sort(s);
+ int count = 0;
+ int start = s.length - 1;
+ // 遍历胃口
+ for (int index = g.length - 1; index >= 0; index--) {
+ if(start >= 0 && g[index] <= s[start]) {
+ start--;
+ count++;
+ }
+ }
+ return count;
+ }
+}
+```
Python:
```python3
class Solution:
+ # 思路1:优先考虑胃饼干
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g.sort()
s.sort()
@@ -145,6 +166,20 @@ class Solution:
res += 1
return res
```
+```python
+class Solution:
+ # 思路2:优先考虑胃口
+ def findContentChildren(self, g: List[int], s: List[int]) -> int:
+ g.sort()
+ s.sort()
+ start, count = len(s) - 1, 0
+ for index in range(len(g) - 1, -1, -1): # 先喂饱大胃口
+ if start >= 0 and g[index] <= s[start]:
+ start -= 1
+ count += 1
+ return count
+```
+
Go:
```golang
//排序后,局部最优
@@ -162,11 +197,10 @@ func findContentChildren(g []int, s []int) int {
return child
}
-
+```
Javascript:
-```Javascript
-
+```
var findContentChildren = function(g, s) {
g = g.sort((a, b) => a - b)
s = s.sort((a, b) => a - b)
@@ -188,4 +222,4 @@ var findContentChildren = function(g, s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md
index 25b52e86..7d8a7286 100644
--- a/problems/0459.重复的子字符串.md
+++ b/problems/0459.重复的子字符串.md
@@ -13,7 +13,7 @@
# 459.重复的子字符串
-https://leetcode-cn.com/problems/repeated-substring-pattern/
+[力扣题目链接](https://leetcode-cn.com/problems/repeated-substring-pattern/)
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
@@ -41,11 +41,11 @@ https://leetcode-cn.com/problems/repeated-substring-pattern/
* [帮你把KMP算法学个通透!(求next数组代码篇)](https://www.bilibili.com/video/BV1M5411j7Xx)
-我们在[字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)里提到了,在一个串中查找是否出现过另一个串,这是KMP的看家本领。
+我们在[字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html)里提到了,在一个串中查找是否出现过另一个串,这是KMP的看家本领。
那么寻找重复子串怎么也涉及到KMP算法了呢?
-这里就要说一说next数组了,next 数组记录的就是最长相同前后缀( [字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg) 这里介绍了什么是前缀,什么是后缀,什么又是最长相同前后缀), 如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。
+这里就要说一说next数组了,next 数组记录的就是最长相同前后缀( [字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html) 这里介绍了什么是前缀,什么是后缀,什么又是最长相同前后缀), 如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。
最长相等前后缀的长度为:next[len - 1] + 1。
@@ -70,7 +70,7 @@ next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此时字符串asdfasdfasdf
C++代码如下:(这里使用了前缀表统一减一的实现方式)
-```C++
+```CPP
class Solution {
public:
void getNext (int* next, const string& s){
@@ -104,7 +104,7 @@ public:
前缀表(不减一)的C++代码实现
-```C++
+```CPP
class Solution {
public:
void getNext (int* next, const string& s){
@@ -137,11 +137,11 @@ public:
# 拓展
-在[字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)中讲解KMP算法的基础理论,给出next数组究竟是如何来了,前缀表又是怎么回事,为什么要选择前缀表。
+在[字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html)中讲解KMP算法的基础理论,给出next数组究竟是如何来了,前缀表又是怎么回事,为什么要选择前缀表。
讲解一道KMP的经典题目,力扣:28. 实现 strStr(),判断文本串里是否出现过模式串,这里涉及到构造next数组的代码实现,以及使用next数组完成模式串与文本串的匹配过程。
-后来很多同学反馈说:搞不懂前后缀,什么又是最长相同前后缀(最长公共前后缀我认为这个用词不准确),以及为什么前缀表要统一减一(右移)呢,不减一行不行?针对这些问题,我在[字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)给出了详细的讲解。
+后来很多同学反馈说:搞不懂前后缀,什么又是最长相同前后缀(最长公共前后缀我认为这个用词不准确),以及为什么前缀表要统一减一(右移)呢,不减一行不行?针对这些问题,我在[字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html)给出了详细的讲解。
## 其他语言版本
@@ -369,4 +369,4 @@ var repeatedSubstringPattern = function (s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0463.岛屿的周长.md b/problems/0463.岛屿的周长.md
index 40378854..609c25a5 100644
--- a/problems/0463.岛屿的周长.md
+++ b/problems/0463.岛屿的周长.md
@@ -1,6 +1,6 @@
## 题目链接
-https://leetcode-cn.com/problems/island-perimeter/
+[力扣题目链接](https://leetcode-cn.com/problems/island-perimeter/)
## 思路
@@ -16,7 +16,7 @@ https://leetcode-cn.com/problems/island-perimeter/
C++代码如下:(详细注释)
-```C++
+```CPP
class Solution {
public:
int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
@@ -56,7 +56,7 @@ result = 岛屿数量 * 4 - cover * 2;
C++代码如下:(详细注释)
-```C++
+```CPP
class Solution {
public:
int islandPerimeter(vector>& grid) {
@@ -84,6 +84,37 @@ public:
Java:
+```java
+// 解法一
+class Solution {
+ // 上下左右 4 个方向
+ int[] dirx = {-1, 1, 0, 0};
+ int[] diry = {0, 0, -1, 1};
+
+ public int islandPerimeter(int[][] grid) {
+ int m = grid.length;
+ int n = grid[0].length;
+ int res = 0; // 岛屿周长
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ if (grid[i][j] == 1) {
+ for (int k = 0; k < 4; k++) {
+ int x = i + dirx[k];
+ int y = j + diry[k];
+ // 当前位置是陆地,并且从当前位置4个方向扩展的“新位置”是“水域”或“新位置“越界,则会为周长贡献一条边
+ if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == 0) {
+ res++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ return res;
+ }
+}
+```
+
Python:
Go:
@@ -95,4 +126,4 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0474.一和零.md b/problems/0474.一和零.md
index 6b73c080..e3b05704 100644
--- a/problems/0474.一和零.md
+++ b/problems/0474.一和零.md
@@ -9,7 +9,7 @@
## 474.一和零
-题目链接:https://leetcode-cn.com/problems/ones-and-zeroes/
+[力扣题目链接](https://leetcode-cn.com/problems/ones-and-zeroes/)
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
@@ -39,6 +39,11 @@
## 思路
+如果对背包问题不都熟悉先看这两篇:
+
+* [动态规划:关于01背包问题,你该了解这些!](https://programmercarl.com/背包理论基础01背包-1.html)
+* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)
+
这道题目,还是比较难的,也有点像程序员自己给自己出个脑筋急转弯,程序员何苦为难程序员呢哈哈。
来说题,本题不少同学会认为是多重背包,一些题解也是这么写的。
@@ -84,18 +89,18 @@ dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
3. dp数组如何初始化
-在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中已经讲解了,01背包的dp数组初始化为0就可以。
+在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)中已经讲解了,01背包的dp数组初始化为0就可以。
因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
4. 确定遍历顺序
-在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中,我们讲到了01背包为什么一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
+在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)中,我们讲到了01背包为什么一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。
代码如下:
-```C++
+```CPP
for (string str : strs) { // 遍历物品
int oneNum = 0, zeroNum = 0;
for (char c : str) {
@@ -126,7 +131,7 @@ for (string str : strs) { // 遍历物品
以上动规五部曲分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int findMaxForm(vector& strs, int m, int n) {
@@ -279,4 +284,4 @@ const findMaxForm = (strs, m, n) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md
index 103b8b10..ea113f4b 100644
--- a/problems/0491.递增子序列.md
+++ b/problems/0491.递增子序列.md
@@ -11,7 +11,7 @@
## 491.递增子序列
-题目链接:https://leetcode-cn.com/problems/increasing-subsequences/
+[力扣题目链接](https://leetcode-cn.com/problems/increasing-subsequences/)
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
@@ -33,11 +33,11 @@
这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。
-这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)。
+这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)。
就是因为太像了,更要注意差别所在,要不就掉坑里了!
-在[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)中我们是通过排序,再加一个标记数组来达到去重的目的。
+在[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)中我们是通过排序,再加一个标记数组来达到去重的目的。
而本题求自增子序列,是不能对原数组经行排序的,排完序的数组都是自增子序列了。
@@ -66,7 +66,7 @@ void backtracking(vector& nums, int startIndex)
* 终止条件
-本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和[回溯算法:求子集问题!](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。
+本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和[回溯算法:求子集问题!](https://programmercarl.com/0078.子集.html)一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。
但本题收集结果有所不同,题目要求递增子序列大小至少为2,所以代码如下:
@@ -105,7 +105,7 @@ for (int i = startIndex; i < nums.size(); i++) {
最后整体C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
private:
@@ -150,7 +150,7 @@ public:
那么优化后的代码如下:
-```C++
+```CPP
// 版本二
class Solution {
private:
@@ -184,7 +184,7 @@ public:
这份代码在leetcode上提交,要比版本一耗时要好的多。
-**所以正如在[哈希表:总结篇!(每逢总结必经典)](https://mp.weixin.qq.com/s/1s91yXtarL-PkX07BfnwLg)中说的那样,数组,set,map都可以做哈希表,而且数组干的活,map和set都能干,但如何数值范围小的话能用数组尽量用数组**。
+**所以正如在[哈希表:总结篇!(每逢总结必经典)](https://programmercarl.com/哈希表总结.html)中说的那样,数组,set,map都可以做哈希表,而且数组干的活,map和set都能干,但如果数值范围小的话能用数组尽量用数组**。
@@ -192,7 +192,7 @@ public:
本题题解清一色都说是深度优先搜索,但我更倾向于说它用回溯法,而且本题我也是完全使用回溯法的逻辑来分析的。
-相信大家在本题中处处都能看到是[回溯算法:求子集问题(二)](https://mp.weixin.qq.com/s/WJ4JNDRJgsW3eUN72Hh3uQ)的身影,但处处又都是陷阱。
+相信大家在本题中处处都能看到是[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)的身影,但处处又都是陷阱。
**对于养成思维定式或者套模板套嗨了的同学,这道题起到了很好的警醒作用。更重要的是拓展了大家的思路!**
@@ -320,4 +320,4 @@ var findSubsequences = function(nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md
index b7da0252..07cf0433 100644
--- a/problems/0494.目标和.md
+++ b/problems/0494.目标和.md
@@ -9,7 +9,7 @@
## 494. 目标和
-题目链接:https://leetcode-cn.com/problems/target-sum/
+[力扣题目链接](https://leetcode-cn.com/problems/target-sum/)
难度:中等
@@ -39,7 +39,12 @@
## 思路
-如果跟着「代码随想录」一起学过[回溯算法系列](https://mp.weixin.qq.com/s/r73thpBnK1tXndFDtlsdCQ)的录友,看到这道题,应该有一种直觉,就是感觉好像回溯法可以爆搜出来。
+如果对背包问题不都熟悉先看这两篇:
+
+* [动态规划:关于01背包问题,你该了解这些!](https://programmercarl.com/背包理论基础01背包-1.html)
+* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)
+
+如果跟着「代码随想录」一起学过[回溯算法系列](https://programmercarl.com/回溯总结.html)的录友,看到这道题,应该有一种直觉,就是感觉好像回溯法可以爆搜出来。
事实确实如此,下面我也会给出相应的代码,只不过会超时,哈哈。
@@ -59,7 +64,7 @@ target是固定的,sum是固定的,left就可以求出来。
## 回溯算法
-在回溯算法系列中,一起学过这道题目[回溯算法:39. 组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)的录友应该感觉很熟悉,这不就是组合总和问题么?
+在回溯算法系列中,一起学过这道题目[回溯算法:39. 组合总和](https://programmercarl.com/0039.组合总和.html)的录友应该感觉很熟悉,这不就是组合总和问题么?
此时可以套组合总和的回溯法代码,几乎不用改动。
@@ -67,7 +72,7 @@ target是固定的,sum是固定的,left就可以求出来。
我也把代码给出来吧,大家可以了解一下,回溯的解法,以下是本题转变为组合总和问题的回溯法代码:
-```C++
+```CPP
class Solution {
private:
vector> result;
@@ -124,11 +129,14 @@ x = (S + sum) / 2
这么担心就对了,例如sum 是5,S是2的话其实就是无解的,所以:
-```C++
+```CPP
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
```
-**看到这种表达式,应该本能的反应,两个int相加数值可能溢出的问题,当然本题并没有溢出**。
+同时如果 S的绝对值已经大于sum,那么也是没有方案的。
+```CPP
+if (abs(S) > sum) return 0; // 此时没有方案
+```
再回归到01背包问题,为什么是01背包呢?
@@ -144,7 +152,7 @@ dp[j] 表示:填满j(包括j)这么大容积的包,有dp[i]种方法
其实也可以使用二维dp数组来求解本题,dp[i][j]:使用 下标为[0, i]的nums[i]能够凑满j(包括j)这么大容量的包,有dp[i][j]种方法。
-下面我都是统一使用一维数组进行讲解, 二维降为一维(滚动数组),其实就是上一层拷贝下来,这个我在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)也有介绍。
+下面我都是统一使用一维数组进行讲解, 二维降为一维(滚动数组),其实就是上一层拷贝下来,这个我在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)也有介绍。
2. 确定递推公式
@@ -179,7 +187,7 @@ dp[j]其他下标对应的数值应该初始化为0,从递归公式也可以
4. 确定遍历顺序
-在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中,我们讲过对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
+在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)中,我们讲过对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
5. 举例推导dp数组
@@ -194,13 +202,13 @@ dp数组状态变化如下:
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int findTargetSumWays(vector& nums, int S) {
int sum = 0;
for (int i = 0; i < nums.size(); i++) sum += nums[i];
- if (S > sum) return 0; // 此时没有方案
+ if (abs(S) > sum) return 0; // 此时没有方案
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
int bagSize = (S + sum) / 2;
vector dp(bagSize + 1, 0);
@@ -221,9 +229,9 @@ public:
## 总结
-此时 大家应该不仅想起,我们之前讲过的[回溯算法:39. 组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)是不是应该也可以用dp来做啊?
+此时 大家应该不仅想起,我们之前讲过的[回溯算法:39. 组合总和](https://programmercarl.com/0039.组合总和.html)是不是应该也可以用dp来做啊?
-是的,如果仅仅是求个数的话,就可以用dp,但[回溯算法:39. 组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)要求的是把所有组合列出来,还是要使用回溯法爆搜的。
+是的,如果仅仅是求个数的话,就可以用dp,但[回溯算法:39. 组合总和](https://programmercarl.com/0039.组合总和.html)要求的是把所有组合列出来,还是要使用回溯法爆搜的。
本题还是有点难度,大家也可以记住,在求装满背包有几种方法的情况下,递推公式一般为:
@@ -248,6 +256,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++) {
@@ -312,7 +321,7 @@ const findTargetSumWays = (nums, target) => {
const sum = nums.reduce((a, b) => a+b);
- if(target > sum) {
+ if(Math.abs(target) > sum) {
return 0;
}
@@ -342,4 +351,4 @@ const findTargetSumWays = (nums, target) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0496.下一个更大元素I.md b/problems/0496.下一个更大元素I.md
index 0404e434..95aca60b 100644
--- a/problems/0496.下一个更大元素I.md
+++ b/problems/0496.下一个更大元素I.md
@@ -1,7 +1,7 @@
# 496.下一个更大元素 I
-题目链接:https://leetcode-cn.com/problems/next-greater-element-i/
+[力扣题目链接](https://leetcode-cn.com/problems/next-greater-element-i/)
给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。
@@ -24,7 +24,7 @@ nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出-1 。
-
+
提示:
* 1 <= nums1.length <= nums2.length <= 1000
@@ -34,13 +34,13 @@ nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位
# 思路
-做本题之前,建议先做一下[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q)
+做本题之前,建议先做一下[739. 每日温度](https://programmercarl.com/0739.每日温度.html)
-在[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q)中是求每个元素下一个比当前元素大的元素的位置。
+在[739. 每日温度](https://programmercarl.com/0739.每日温度.html)中是求每个元素下一个比当前元素大的元素的位置。
本题则是说nums1 是 nums2的子集,找nums1中的元素在nums2中下一个比当前元素大的元素。
-看上去和[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q) 就如出一辙了。
+看上去和[739. 每日温度](https://programmercarl.com/0739.每日温度.html) 就如出一辙了。
几乎是一样的,但是这么绕了一下,其实还上升了一点难度。
@@ -60,11 +60,11 @@ nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位
没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
-C++中,当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的。我在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中也做了详细的解释。
+C++中,当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的。我在[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)中也做了详细的解释。
那么预处理代码如下:
-```C++
+```CPP
unordered_map umap; // key:下表元素,value:下表
for (int i = 0; i < nums1.size(); i++) {
umap[nums1[i]] = i;
@@ -100,7 +100,7 @@ for (int i = 0; i < nums1.size(); i++) {
代码如下:
-```C++
+```CPP
while (!st.empty() && nums2[i] > nums2[st.top()]) {
if (umap.count(nums2[st.top()]) > 0) { // 看map里是否存在这个元素
int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下表
@@ -114,7 +114,7 @@ st.push(i);
以上分析完毕,C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -152,7 +152,7 @@ public:
针对版本一,进行代码精简后,代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -186,7 +186,37 @@ public:
建议大家把情况一二三想清楚了,先写出版本一的代码,然后在其基础上在做精简!
## 其他语言版本
+Java
+```java
+class Solution {
+ public int[] nextGreaterElement(int[] nums1, int[] nums2) {
+ Stack temp = new Stack<>();
+ int[] res = new int[nums1.length];
+ Arrays.fill(res,-1);
+ HashMap hashMap = new HashMap<>();
+ for (int i = 0 ; i< nums1.length ; i++){
+ hashMap.put(nums1[i],i);
+ }
+ temp.add(0);
+ for (int i = 1; i < nums2.length; i++) {
+ if (nums2[i] <= nums2[temp.peek()]) {
+ temp.add(i);
+ } else {
+ while (!temp.isEmpty() && nums2[temp.peek()] < nums2[i]) {
+ if (hashMap.containsKey(nums2[temp.peek()])){
+ Integer index = hashMap.get(nums2[temp.peek()]);
+ res[index] = nums2[i];
+ }
+ temp.pop();
+ }
+ temp.add(i);
+ }
+ }
+ return res;
+ }
+}
+```
Python:
```python3
class Solution:
@@ -208,3 +238,4 @@ class Solution:
return result
```
+
diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md
index ac7ff603..22d4f4ac 100644
--- a/problems/0501.二叉搜索树中的众数.md
+++ b/problems/0501.二叉搜索树中的众数.md
@@ -9,9 +9,9 @@
> 二叉树上应该怎么求,二叉搜索树上又应该怎么求?
-## 501.二叉搜索树中的众数
+# 501.二叉搜索树中的众数
-题目地址:https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/solution/
+[力扣题目链接](https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/solution/)
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
@@ -33,7 +33,7 @@
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
-## 思路
+# 思路
这道题目呢,递归法我从两个维度来讲。
@@ -53,7 +53,7 @@
这里采用前序遍历,代码如下:
-```C++
+```CPP
// map key:元素,value:出现频率
void searchBST(TreeNode* cur, unordered_map& map) { // 前序遍历
if (cur == NULL) return ;
@@ -87,7 +87,7 @@ sort(vec.begin(), vec.end(), cmp); // 给频率排个序
代码如下:
-```C++
+```CPP
result.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i++) {
// 取最高的放到result数组中
@@ -100,7 +100,7 @@ return result;
整体C++代码如下:
-```C++
+```CPP
class Solution {
private:
@@ -145,7 +145,7 @@ public:
中序遍历代码如下:
-```C++
+```CPP
void searchBST(TreeNode* cur) {
if (cur == NULL) return ;
searchBST(cur->left); // 左
@@ -161,7 +161,7 @@ void searchBST(TreeNode* cur) {
这就考察对树的操作了。
-在[二叉树:搜索树的最小绝对差](https://mp.weixin.qq.com/s/Hwzml6698uP3qQCC1ctUQQ)中我们就使用了pre指针和cur指针的技巧,这次又用上了。
+在[二叉树:搜索树的最小绝对差](https://programmercarl.com/0530.二叉搜索树的最小绝对差.html)中我们就使用了pre指针和cur指针的技巧,这次又用上了。
弄一个指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。
@@ -217,7 +217,7 @@ if (count > maxCount) { // 如果计数大于最大值
关键代码都讲完了,完整代码如下:(**只需要遍历一遍二叉搜索树,就求出了众数的集合**)
-```C++
+```CPP
class Solution {
private:
int maxCount; // 最大频率
@@ -272,14 +272,14 @@ public:
二叉树前中后序转迭代,传送门:
-* [二叉树:前中后序迭代法](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)
-* [二叉树:前中后序统一风格的迭代方式](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)
+* [二叉树:前中后序迭代法](https://programmercarl.com/二叉树的迭代遍历.html)
+* [二叉树:前中后序统一风格的迭代方式](https://programmercarl.com/二叉树的统一迭代法.html)
下面我给出其中的一种中序遍历的迭代法,其中间处理逻辑一点都没有变(我从递归法直接粘过来的代码,连注释都没改,哈哈)
代码如下:
-```C++
+```CPP
class Solution {
public:
vector findMode(TreeNode* root) {
@@ -321,7 +321,7 @@ public:
};
```
-## 总结
+# 总结
本题在递归法中,我给出了如果是普通二叉树,应该怎么求众数。
@@ -340,12 +340,13 @@ public:
> **需要强调的是 leetcode上的耗时统计是非常不准确的,看个大概就行,一样的代码耗时可以差百分之50以上**,所以leetcode的耗时统计别太当回事,知道理论上的效率优劣就行了。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
暴力法
+
```java
class Solution {
public int[] findMode(FindModeInBinarySearchTree.TreeNode root) {
@@ -379,6 +380,8 @@ class Solution {
}
```
+中序遍历-不使用额外空间,利用二叉搜索树特性
+
```Java
class Solution {
ArrayList resList;
@@ -426,16 +429,50 @@ class Solution {
}
}
```
+迭代法
+```java
+class Solution {
+ public int[] findMode(TreeNode root) {
+ TreeNode pre = null;
+ Stack stack = new Stack<>();
+ List result = new ArrayList<>();
+ int maxCount = 0;
+ int count = 0;
+ TreeNode cur = root;
+ while (cur != null || !stack.isEmpty()) {
+ if (cur != null) {
+ stack.push(cur);
+ cur =cur.left;
+ }else {
+ cur = stack.pop();
+ // 计数
+ if (pre == null || cur.val != pre.val) {
+ count = 1;
+ }else {
+ count++;
+ }
+ // 更新结果
+ if (count > maxCount) {
+ maxCount = count;
+ result.clear();
+ result.add(cur.val);
+ }else if (count == maxCount) {
+ result.add(cur.val);
+ }
+ pre = cur;
+ cur = cur.right;
+ }
+ }
+ return result.stream().mapToInt(Integer::intValue).toArray();
+ }
+}
+```
+
+## Python
+
+递归法
-Python:
```python
-# 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 findMode(self, root: TreeNode) -> List[int]:
if not root: return
@@ -460,36 +497,11 @@ class Solution:
return
findNumber(root)
return self.res
+```
-# 迭代法-中序遍历-使用额外空间map的方法:
-class Solution:
- def findMode(self, root: TreeNode) -> List[int]:
- stack = []
- cur = root
- pre = None
- dist = {}
- while cur or stack:
- if cur: # 指针来访问节点,访问到最底层
- stack.append(cur)
- cur = cur.left
- else: # 逐一处理节点
- cur = stack.pop()
- if cur.val in dist:
- dist[cur.val] += 1
- else:
- dist[cur.val] = 1
- pre = cur
- cur = cur.right
-
- # 找出字典中最大的key
- res = []
- for key, value in dist.items():
- if (value == max(dist.values())):
- res.append(key)
- return res
-
-# 迭代法-中序遍历-不使用额外空间,利用二叉搜索树特性:
+迭代法-中序遍历-不使用额外空间,利用二叉搜索树特性
+```python
class Solution:
def findMode(self, root: TreeNode) -> List[int]:
stack = []
@@ -521,18 +533,11 @@ class Solution:
return res
```
-Go:
+## Go
+
暴力法(非BSL)
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
func findMode(root *TreeNode) []int {
var history map[int]int
var maxValue int
@@ -571,15 +576,7 @@ func traversal(root *TreeNode,history map[int]int){
计数法,不使用额外空间,利用二叉树性质,中序遍历
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
- func findMode(root *TreeNode) []int {
+func findMode(root *TreeNode) []int {
res := make([]int, 0)
count := 1
max := 1
@@ -611,8 +608,9 @@ func traversal(root *TreeNode,history map[int]int){
}
```
-JavaScript版本:
-使用额外空间map的方法:
+## JavaScript
+
+使用额外空间map的方法
```javascript
var findMode = function(root) {
// 使用递归中序遍历
@@ -649,8 +647,10 @@ var findMode = function(root) {
}
return res;
};
-```
+```
+
不使用额外空间,利用二叉树性质,中序遍历(有序):
+
```javascript
var findMode = function(root) {
// 不使用额外空间,使用中序遍历,设置出现最大次数初始值为1
@@ -690,4 +690,4 @@ var findMode = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0503.下一个更大元素II.md b/problems/0503.下一个更大元素II.md
index 34ade48e..624c6c7c 100644
--- a/problems/0503.下一个更大元素II.md
+++ b/problems/0503.下一个更大元素II.md
@@ -1,7 +1,7 @@
# 503.下一个更大元素II
-链接:https://leetcode-cn.com/problems/next-greater-element-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/next-greater-element-ii/)
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
@@ -14,13 +14,13 @@
# 思路
-做本题之前建议先做[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q) 和 [496.下一个更大元素 I](https://mp.weixin.qq.com/s/U0O6XkFOe-RMXthPS16sWQ)。
+做本题之前建议先做[739. 每日温度](https://programmercarl.com/0739.每日温度.html) 和 [496.下一个更大元素 I](https://programmercarl.com/0496.下一个更大元素I.html)。
-这道题和[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q)也几乎如出一辙。
+这道题和[739. 每日温度](https://programmercarl.com/0739.每日温度.html)也几乎如出一辙。
不同的时候本题要循环数组了。
-关于单调栈的讲解我在题解[739. 每日温度](https://mp.weixin.qq.com/s/YeQ7eE0-hZpxJfJJziq25Q)中已经详细讲解了。
+关于单调栈的讲解我在题解[739. 每日温度](https://programmercarl.com/0739.每日温度.html)中已经详细讲解了。
本篇我侧重与说一说,如何处理循环数组。
@@ -32,7 +32,7 @@
代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -69,7 +69,7 @@ resize倒是不费时间,是O(1)的操作,但扩充nums数组相当于多了
代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -95,6 +95,28 @@ public:
## 其他语言版本
Java:
+```Java
+class Solution {
+ public int[] nextGreaterElements(int[] nums) {
+ //边界判断
+ if(nums == null || nums.length <= 1) {
+ return new int[]{-1};
+ }
+ int size = nums.length;
+ int[] result = new int[size];//存放结果
+ Arrays.fill(result,-1);//默认全部初始化为-1
+ Stack st= new Stack<>();//栈中存放的是nums中的元素下标
+ for(int i = 0; i < 2*size; i++) {
+ while(!st.empty() && nums[i % size] > nums[st.peek()]) {
+ result[st.peek()] = nums[i % size];//更新result
+ st.pop();//弹出栈顶
+ }
+ st.push(i % size);
+ }
+ return result;
+ }
+}
+```
Python:
```python3
@@ -110,5 +132,50 @@ class Solution:
return dp
```
Go:
+```go
+func nextGreaterElements(nums []int) []int {
+ length := len(nums)
+ result := make([]int,length,length)
+ for i:=0;i0&&nums[i%length]>nums[stack[len(stack)-1]]{
+ index := stack[len(stack)-1]
+ stack = stack[:len(stack)-1] // pop
+ result[index] = nums[i%length]
+ }
+ stack = append(stack,i%length)
+ }
+ return result
+}
+```
JavaScript:
+
+```JS
+/**
+ * @param {number[]} nums
+ * @return {number[]}
+ */
+var nextGreaterElements = function (nums) {
+ // let map = new Map();
+ let stack = [];
+ let res = new Array(nums.length).fill(-1);
+ for (let i = 0; i < nums.length * 2; i++) {
+ while (
+ stack.length &&
+ nums[i % nums.length] > nums[stack[stack.length - 1]]
+ ) {
+ let index = stack.pop();
+ res[index] = nums[i % nums.length];
+ }
+ stack.push(i % nums.length);
+ }
+
+ return res;
+};
+```
+
diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md
index dddac899..7e4df26c 100644
--- a/problems/0509.斐波那契数.md
+++ b/problems/0509.斐波那契数.md
@@ -8,7 +8,7 @@
## 509. 斐波那契数
-题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+[力扣题目链接](https://leetcode-cn.com/problems/fibonacci-number/)
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
@@ -29,7 +29,7 @@ F(n) = F(n - 1) + F(n - 2),其中 n > 1
输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3
-
+
提示:
* 0 <= n <= 30
@@ -47,7 +47,7 @@ F(n) = F(n - 1) + F(n - 2),其中 n > 1
对于动规,如果没有方法论的话,可能简单题目可以顺手一写就过,难一点就不知道如何下手了。
-所以我总结的动规五部曲,是要用来贯穿整个动态规划系列的,就像之前讲过[二叉树系列的递归三部曲](https://mp.weixin.qq.com/s/I6ZXFbw09NR31F5CJR_geQ),[回溯法系列的回溯三部曲](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)一样。后面慢慢大家就会体会到,动规五部曲方法的重要性。
+所以我总结的动规五部曲,是要用来贯穿整个动态规划系列的,就像之前讲过[二叉树系列的递归三部曲](https://programmercarl.com/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.html),[回溯法系列的回溯三部曲](https://programmercarl.com/回溯算法理论基础.html)一样。后面慢慢大家就会体会到,动规五部曲方法的重要性。
### 动态规划
@@ -88,7 +88,7 @@ dp[1] = 1;
以上我们用动规的方法分析完了,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int fib(int N) {
@@ -110,7 +110,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
int fib(int N) {
@@ -137,7 +137,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
int fib(int N) {
@@ -150,14 +150,14 @@ public:
* 时间复杂度:O(2^n)
* 空间复杂度:O(n) 算上了编程语言中实现递归的系统栈所占空间
-这个递归的时间复杂度大家画一下树形图就知道了,如果不清晰的同学,可以看这篇:[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://mp.weixin.qq.com/s/I6ZXFbw09NR31F5CJR_geQ)
+这个递归的时间复杂度大家画一下树形图就知道了,如果不清晰的同学,可以看这篇:[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://programmercarl.com/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.html)
# 总结
斐波那契数列这道题目是非常基础的题目,我在后面的动态规划的讲解中将会多次提到斐波那契数列!
-这里我严格按照[关于动态规划,你该了解这些!](https://leetcode-cn.com/circle/article/tNuNnM/)中的动规五部曲来分析了这道题目,一些分析步骤可能同学感觉没有必要搞的这么复杂,代码其实上来就可以撸出来。
+这里我严格按照[关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html)中的动规五部曲来分析了这道题目,一些分析步骤可能同学感觉没有必要搞的这么复杂,代码其实上来就可以撸出来。
但我还是强调一下,简单题是用来掌握方法论的,动规五部曲将在接下来的动态规划讲解中发挥重要作用,敬请期待!
@@ -167,7 +167,7 @@ public:
-## 其他语言版本
+# 其他语言版本
Java:
@@ -238,4 +238,4 @@ var fib = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0513.找树左下角的值.md b/problems/0513.找树左下角的值.md
index 17d15fde..d2c05fd8 100644
--- a/problems/0513.找树左下角的值.md
+++ b/problems/0513.找树左下角的值.md
@@ -7,7 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 513.找树左下角的值
+# 513.找树左下角的值
+
+[力扣题目链接](https://leetcode-cn.com/problems/find-bottom-left-tree-value/)
给定一个二叉树,在树的最后一行找到最左边的值。
@@ -19,7 +21,7 @@

-## 思路
+# 思路
本地要找出树的最后一行找到最左边的值。此时大家应该想起用层序遍历是非常简单的了,反而用递归的话会比较难一点。
@@ -37,7 +39,7 @@
如果使用递归法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。
-如果对二叉树深度和高度还有点疑惑的话,请看:[110.平衡二叉树](https://mp.weixin.qq.com/s/isUS-0HDYknmC0Rr4R8mww)。
+如果对二叉树深度和高度还有点疑惑的话,请看:[110.平衡二叉树](https://programmercarl.com/0110.平衡二叉树.html)。
所以要找深度最大的叶子节点。
@@ -53,7 +55,7 @@
代码如下:
-```
+```CPP
int maxLen = INT_MIN; // 全局变量 记录最大深度
int maxleftValue; // 全局变量 最大深度最左节点的数值
void traversal(TreeNode* root, int leftLen)
@@ -75,7 +77,7 @@ void traversal(TreeNode* root, int leftLen)
代码如下:
-```
+```CPP
if (root->left == NULL && root->right == NULL) {
if (leftLen > maxLen) {
maxLen = leftLen; // 更新最大深度
@@ -89,7 +91,7 @@ if (root->left == NULL && root->right == NULL) {
在找最大深度的时候,递归的过程中依然要使用回溯,代码如下:
-```C++
+```CPP
// 中
if (root->left) { // 左
leftLen++; // 深度加一
@@ -106,7 +108,7 @@ return;
完整代码如下:
-```C++
+```CPP
class Solution {
public:
int maxLen = INT_MIN;
@@ -140,7 +142,7 @@ public:
当然回溯的地方可以精简,精简代码如下:
-```C++
+```CPP
class Solution {
public:
int maxLen = INT_MIN;
@@ -168,7 +170,7 @@ public:
};
```
-如果对回溯部分精简的代码 不理解的话,可以看这篇[二叉树:找我的所有路径?](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)和[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA) 。这两篇文章详细分析了回溯隐藏在了哪里。
+如果对回溯部分精简的代码 不理解的话,可以看这篇[257. 二叉树的所有路径](https://programmercarl.com/0257.二叉树的所有路径.html)
## 迭代法
@@ -177,11 +179,11 @@ public:
只需要记录最后一行第一个节点的数值就可以了。
-如果对层序遍历不了解,看这篇[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog),这篇里也给出了层序遍历的模板,稍作修改就一过刷了这道题了。
+如果对层序遍历不了解,看这篇[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html),这篇里也给出了层序遍历的模板,稍作修改就一过刷了这道题了。
代码如下:
-```C++
+```CPP
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
@@ -203,20 +205,20 @@ public:
};
```
-## 总结
+# 总结
本题涉及如下几点:
-* 递归求深度的写法,我们在[110.平衡二叉树](https://mp.weixin.qq.com/s/isUS-0HDYknmC0Rr4R8mww)中详细的分析了深度应该怎么求,高度应该怎么求。
-* 递归中其实隐藏了回溯,在[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA)中讲解了究竟哪里使用了回溯,哪里隐藏了回溯。
-* 层次遍历,在[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)深度讲解了二叉树层次遍历。
+* 递归求深度的写法,我们在[110.平衡二叉树](https://programmercarl.com/0110.平衡二叉树.html)中详细的分析了深度应该怎么求,高度应该怎么求。
+* 递归中其实隐藏了回溯,在[257. 二叉树的所有路径](https://programmercarl.com/0257.二叉树的所有路径.html)中讲解了究竟哪里使用了回溯,哪里隐藏了回溯。
+* 层次遍历,在[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)深度讲解了二叉树层次遍历。
所以本题涉及到的点,我们之前都讲解过,这些知识点需要同学们灵活运用,这样就举一反三了。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```java
// 递归法
@@ -273,42 +275,60 @@ class Solution {
-Python:
+## Python
+
+递归:
```python
-//递归法
-# 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 findBottomLeftValue(self, root: TreeNode) -> int:
- depth=0
- self.res=[]
- def level(root,depth):
- if not root:return
- if depth==len(self.res):
- self.res.append([])
- self.res[depth].append(root.val)
- level(root.left,depth+1)
- level(root.right,depth+1)
- level(root,depth)
- return self.res[-1][0]
-```
-Go:
+ max_depth = -float("INF")
+ leftmost_val = 0
-> 递归法
+ def __traverse(root, cur_depth):
+ nonlocal max_depth, leftmost_val
+ if not root.left and not root.right:
+ if cur_depth > max_depth:
+ max_depth = cur_depth
+ leftmost_val = root.val
+ if root.left:
+ cur_depth += 1
+ __traverse(root.left, cur_depth)
+ cur_depth -= 1
+ if root.right:
+ cur_depth += 1
+ __traverse(root.right, cur_depth)
+ cur_depth -= 1
+
+ __traverse(root, 0)
+ return leftmost_val
+```
+
+迭代 - 层序遍历:
+```python
+class Solution:
+ def findBottomLeftValue(self, root: TreeNode) -> int:
+ queue = deque()
+ if root:
+ queue.append(root)
+ result = 0
+ while queue:
+ q_len = len(queue)
+ for i in range(q_len):
+ if i == 0:
+ result = queue[i].val
+ cur = queue.popleft()
+ if cur.left:
+ queue.append(cur.left)
+ if cur.right:
+ queue.append(cur.right)
+ return result
+```
+
+## Go
+
+递归法:
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
var maxDeep int // 全局变量 深度
var value int //全局变量 最终值
func findBottomLeftValue(root *TreeNode) int {
@@ -340,17 +360,9 @@ func findLeftValue (root *TreeNode,deep int){
}
```
-> 迭代法
+迭代法:
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
func findBottomLeftValue(root *TreeNode) int {
queue:=list.New()
var gradation int
@@ -372,8 +384,10 @@ func findBottomLeftValue(root *TreeNode) int {
}
```
-JavaScript:
-1. 递归版本
+## JavaScript
+
+递归版本:
+
```javascript
var findBottomLeftValue = function(root) {
//首先考虑递归遍历 前序遍历 找到最大深度的叶子节点即可
@@ -395,7 +409,8 @@ var findBottomLeftValue = function(root) {
return resNode;
};
```
-2. 层序遍历
+层序遍历:
+
```javascript
var findBottomLeftValue = function(root) {
//考虑层序遍历 记录最后一行的第一个节点
@@ -426,4 +441,4 @@ var findBottomLeftValue = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0516.最长回文子序列.md b/problems/0516.最长回文子序列.md
index 388d8d6a..89b5667f 100644
--- a/problems/0516.最长回文子序列.md
+++ b/problems/0516.最长回文子序列.md
@@ -7,7 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
## 516.最长回文子序列
-题目链接:https://leetcode-cn.com/problems/longest-palindromic-subsequence/
+[力扣题目链接](https://leetcode-cn.com/problems/longest-palindromic-subsequence/)
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
@@ -29,7 +29,7 @@
## 思路
-我们刚刚做过了 [动态规划:回文子串](https://mp.weixin.qq.com/s/2WetyP6IYQ6VotegepVpEw),求的是回文子串,而本题要求的是回文子序列, 要搞清楚这两者之间的区别。
+我们刚刚做过了 [动态规划:回文子串](https://programmercarl.com/0647.回文子串.html),求的是回文子串,而本题要求的是回文子序列, 要搞清楚这两者之间的区别。
**回文子串是要连续的,回文子序列可不是连续的!** 回文子串,回文子序列都是动态规划经典题目。
@@ -69,7 +69,7 @@
代码如下:
-```C++
+```CPP
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
@@ -85,7 +85,7 @@ if (s[i] == s[j]) {
其他情况dp[i][j]初始为0就行,这样递推公式:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 中dp[i][j]才不会被初始值覆盖。
-```C++
+```CPP
vector> dp(s.size(), vector(s.size(), 0));
for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
```
@@ -102,7 +102,7 @@ for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
代码如下:
-```C++
+```CPP
for (int i = s.size() - 1; i >= 0; i--) {
for (int j = i + 1; j < s.size(); j++) {
if (s[i] == s[j]) {
@@ -124,7 +124,7 @@ for (int i = s.size() - 1; i >= 0; i--) {
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int longestPalindromeSubseq(string s) {
@@ -243,4 +243,4 @@ const longestPalindromeSubseq = (s) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md
index 3f907da9..6eb31cd8 100644
--- a/problems/0518.零钱兑换II.md
+++ b/problems/0518.零钱兑换II.md
@@ -9,7 +9,7 @@
## 518. 零钱兑换 II
-链接:https://leetcode-cn.com/problems/coin-change-2/
+[力扣题目链接](https://leetcode-cn.com/problems/coin-change-2/)
难度:中等
@@ -46,7 +46,8 @@
这是一道典型的背包问题,一看到钱币数量不限,就知道这是一个完全背包。
-对完全背包还不了解的同学,可以看这篇:[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)
+
+对完全背包还不了解的同学,可以看这篇:[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)
但本题和纯完全背包不一样,**纯完全背包是能否凑成总金额,而本题是要求凑成总金额的个数!**
@@ -78,7 +79,7 @@ dp[j] (考虑coins[i]的组合总和) 就是所有的dp[j - coins[i]](不
所以递推公式:dp[j] += dp[j - coins[i]];
-**这个递推公式大家应该不陌生了,我在讲解01背包题目的时候在这篇[动态规划:目标和!](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw)中就讲解了,求装满背包有几种方法,一般公式都是:dp[j] += dp[j - nums[i]];**
+**这个递推公式大家应该不陌生了,我在讲解01背包题目的时候在这篇[动态规划:目标和!](https://programmercarl.com/0494.目标和.html)中就讲解了,求装满背包有几种方法,一般公式都是:dp[j] += dp[j - nums[i]];**
3. dp数组如何初始化
@@ -93,7 +94,7 @@ dp[j] (考虑coins[i]的组合总和) 就是所有的dp[j - coins[i]](不
本题中我们是外层for循环遍历物品(钱币),内层for遍历背包(金钱总额),还是外层for遍历背包(金钱总额),内层for循环遍历物品(钱币)呢?
-我在[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)中讲解了完全背包的两个for循环的先后顺序都是可以的。
+我在[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)中讲解了完全背包的两个for循环的先后顺序都是可以的。
**但本题就不行了!**
@@ -111,7 +112,7 @@ dp[j] (考虑coins[i]的组合总和) 就是所有的dp[j - coins[i]](不
代码如下:
-```C++
+```CPP
for (int i = 0; i < coins.size(); i++) { // 遍历物品
for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
dp[j] += dp[j - coins[i]];
@@ -151,7 +152,7 @@ for (int j = 0; j <= amount; j++) { // 遍历背包容量
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int change(int amount, vector& coins) {
@@ -170,7 +171,7 @@ public:
## 总结
-本题的递推公式,其实我们在[动态规划:目标和!](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw)中就已经讲过了,**而难点在于遍历顺序!**
+本题的递推公式,其实我们在[动态规划:目标和!](https://programmercarl.com/0494.目标和.html)中就已经讲过了,**而难点在于遍历顺序!**
在求装满背包有几种方案的时候,认清遍历顺序是非常关键的。
@@ -265,4 +266,4 @@ const change = (amount, coins) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0530.二叉搜索树的最小绝对差.md b/problems/0530.二叉搜索树的最小绝对差.md
index bf646443..b19a0dd2 100644
--- a/problems/0530.二叉搜索树的最小绝对差.md
+++ b/problems/0530.二叉搜索树的最小绝对差.md
@@ -9,9 +9,9 @@
> 利用二叉搜索树的特性搞起!
-## 530.二叉搜索树的最小绝对差
+# 530.二叉搜索树的最小绝对差
-题目地址:https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/
+[力扣题目链接](https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/)
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
@@ -21,7 +21,7 @@
提示:树中至少有 2 个节点。
-## 思路
+# 思路
题目中要求在二叉搜索树上任意两节点的差的绝对值的最小值。
@@ -39,7 +39,7 @@
代码如下:
-```C++
+```CPP
class Solution {
private:
vector vec;
@@ -75,7 +75,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
private:
int result = INT_MAX;
@@ -101,11 +101,11 @@ public:
## 迭代
-看过这两篇[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg),[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)文章之后,不难写出两种中序遍历的迭代法。
+看过这两篇[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html),[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://programmercarl.com/二叉树的统一迭代法.html)文章之后,不难写出两种中序遍历的迭代法。
下面我给出其中的一种中序遍历的迭代法,代码如下:
-```C++
+```CPP
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
@@ -132,7 +132,7 @@ public:
};
```
-## 总结
+# 总结
**遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点。**
@@ -142,15 +142,11 @@ public:
+# 其他语言版本
+## Java
-
-
-## 其他语言版本
-
-
-Java:
递归
```java
class Solution {
@@ -175,38 +171,38 @@ class Solution {
}
}
```
-```Java
+迭代法-中序遍历
+
+```java
class Solution {
- TreeNode pre;// 记录上一个遍历的结点
- int result = Integer.MAX_VALUE;
+ TreeNode pre;
+ Stack stack;
public int getMinimumDifference(TreeNode root) {
- if (root == null) {
- return result;
+ if (root == null) return 0;
+ stack = new Stack<>();
+ TreeNode cur = root;
+ int result = Integer.MAX_VALUE;
+ while (cur != null || !stack.isEmpty()) {
+ if (cur != null) {
+ stack.push(cur); // 将访问的节点放进栈
+ cur = cur.left; // 左
+ }else {
+ cur = stack.pop();
+ if (pre != null) { // 中
+ result = Math.min(result, cur.val - pre.val);
+ }
+ pre = cur;
+ cur = cur.right; // 右
+ }
}
- // 左
- int left = getMinimumDifference(root.left);
-
- // 中
- if (pre != null) {
- result = Math.min(left, root.val - pre.val);
- }
- pre = root;
- // 右
- int right = getMinimumDifference(root.right);
- result = Math.min(right, result);
return result;
}
}
```
+## Python
-Python:
+递归
```python
-# 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 getMinimumDifference(self, root: TreeNode) -> int:
res = []
@@ -222,8 +218,10 @@ class Solution:
for i in range(len(res)-1): // 统计有序数组的最小差值
r = min(abs(res[i]-res[i+1]),r)
return r
-
-# 迭代法-中序遍历
+```
+
+迭代法-中序遍历
+```python
class Solution:
def getMinimumDifference(self, root: TreeNode) -> int:
stack = []
@@ -242,19 +240,13 @@ class Solution:
cur = cur.right
return result
-```
-Go:
-> 中序遍历,然后计算最小差值
+```
+
+## Go:
+
+中序遍历,然后计算最小差值
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
func getMinimumDifference(root *TreeNode) int {
var res []int
findMIn(root,&res)
@@ -299,8 +291,8 @@ func getMinimumDifference(root *TreeNode) int {
}
```
-JavaScript版本
-
+## JavaScript
+递归 先转换为有序数组
```javascript
/**
* Definition for a binary tree node.
@@ -332,9 +324,50 @@ var getMinimumDifference = function (root) {
return diff;
};
```
+递归 在递归的过程中更新最小值
+```js
+var getMinimumDifference = function(root) {
+ let res = Infinity
+ let preNode = null
+ // 中序遍历
+ const inorder = (node) => {
+ if(!node) return
+ inorder(node.left)
+ // 更新res
+ if(preNode) res = Math.min(res, node.val - preNode.val)
+ // 记录前一个节点
+ preNode = node
+ inorder(node.right)
+ }
+ inorder(root)
+ return res
+}
+```
+
+迭代 中序遍历
+```js
+var getMinimumDifference = function(root) {
+ let stack = []
+ let cur = root
+ let res = Infinity
+ let pre = null
+ while(cur || stack.length) {
+ if(cur) {
+ stack.push(cur)
+ cur = cur.left
+ } else {
+ cur = stack.pop()
+ if(pre) res = Math.min(res, cur.val - pre.val)
+ pre = cur
+ cur = cur.right
+ }
+ }
+ return res
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md
index 3ecb8195..24fc7211 100644
--- a/problems/0538.把二叉搜索树转换为累加树.md
+++ b/problems/0538.把二叉搜索树转换为累加树.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 538.把二叉搜索树转换为累加树
+# 538.把二叉搜索树转换为累加树
-题目链接:https://leetcode-cn.com/problems/convert-bst-to-greater-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/convert-bst-to-greater-tree/)
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
@@ -23,20 +23,20 @@

-输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
-输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
+* 输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
+* 输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
示例 2:
-输入:root = [0,null,1]
-输出:[1,null,1]
+* 输入:root = [0,null,1]
+* 输出:[1,null,1]
示例 3:
-输入:root = [1,0,2]
-输出:[3,3,2]
+* 输入:root = [1,0,2]
+* 输出:[3,3,2]
示例 4:
-输入:root = [3,2,4,1]
-输出:[7,9,4,10]
+* 输入:root = [3,2,4,1]
+* 输出:[7,9,4,10]
提示:
@@ -45,7 +45,7 @@
* 树中的所有值 互不相同 。
* 给定的树为二叉搜索树。
-## 思路
+# 思路
一看到累加树,相信很多小伙伴都会疑惑:如何累加?遇到一个节点,然后在遍历其他节点累加?怎么一想这么麻烦呢。
@@ -69,7 +69,7 @@
本题依然需要一个pre指针记录当前遍历节点cur的前一个节点,这样才方便做累加。
-pre指针的使用技巧,我们在[二叉树:搜索树的最小绝对差](https://mp.weixin.qq.com/s/Hwzml6698uP3qQCC1ctUQQ)和[二叉树:我的众数是多少?](https://mp.weixin.qq.com/s/KSAr6OVQIMC-uZ8MEAnGHg)都提到了,这是常用的操作手段。
+pre指针的使用技巧,我们在[二叉树:搜索树的最小绝对差](https://programmercarl.com/0530.二叉搜索树的最小绝对差.html)和[二叉树:我的众数是多少?](https://programmercarl.com/0501.二叉搜索树中的众数.html)都提到了,这是常用的操作手段。
* 递归函数参数以及返回值
@@ -107,7 +107,7 @@ traversal(cur->left); // 左
递归法整体代码如下:
-```C++
+```CPP
class Solution {
private:
int pre; // 记录前一个节点的数值
@@ -129,11 +129,11 @@ public:
## 迭代法
-迭代法其实就是中序模板题了,在[二叉树:前中后序迭代法](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序统一方式迭代法](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)可以选一种自己习惯的写法。
+迭代法其实就是中序模板题了,在[二叉树:前中后序迭代法](https://programmercarl.com/二叉树的迭代遍历.html)和[二叉树:前中后序统一方式迭代法](https://programmercarl.com/二叉树的统一迭代法.html)可以选一种自己习惯的写法。
这里我给出其中的一种,代码如下:
-```C++
+```CPP
class Solution {
private:
int pre; // 记录前一个节点的数值
@@ -162,17 +162,17 @@ public:
};
```
-## 总结
+# 总结
经历了前面各种二叉树增删改查的洗礼之后,这道题目应该比较简单了。
**好了,二叉树已经接近尾声了,接下来就是要对二叉树来一个大总结了**。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
int sum;
@@ -195,15 +195,10 @@ class Solution {
}
```
-Python:
-```python3
-# 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
-#递归法
+## Python
+
+递归法
+```python
class Solution:
def convertBST(self, root: TreeNode) -> TreeNode:
def buildalist(root):
@@ -216,10 +211,10 @@ class Solution:
buildalist(root)
return root
```
-Go:
+## Go
-> 弄一个sum暂存其和值
+弄一个sum暂存其和值
```go
//右中左
@@ -239,23 +234,10 @@ func RightMLeft(root *TreeNode,sum *int) *TreeNode {
}
```
-JavaScript版本
-
-> 递归
+## JavaScript
+递归
```javascript
-/**
- * Definition for a binary tree node.
- * function TreeNode(val, left, right) {
- * this.val = (val===undefined ? 0 : val)
- * this.left = (left===undefined ? null : left)
- * this.right = (right===undefined ? null : right)
- * }
- */
-/**
- * @param {TreeNode} root
- * @return {TreeNode}
- */
var convertBST = function(root) {
let pre = 0;
const ReverseInOrder = (cur) => {
@@ -271,21 +253,8 @@ var convertBST = function(root) {
};
```
-> 迭代
-
+迭代
```javascript
-/**
- * Definition for a binary tree node.
- * function TreeNode(val, left, right) {
- * this.val = (val===undefined ? 0 : val)
- * this.left = (left===undefined ? null : left)
- * this.right = (right===undefined ? null : right)
- * }
- */
-/**
- * @param {TreeNode} root
- * @return {TreeNode}
- */
var convertBST = function (root) {
let pre = 0;
let cur = root;
@@ -308,4 +277,4 @@ var convertBST = function (root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0541.反转字符串II.md b/problems/0541.反转字符串II.md
index 47e85161..df2fade8 100644
--- a/problems/0541.反转字符串II.md
+++ b/problems/0541.反转字符串II.md
@@ -12,7 +12,7 @@
# 541. 反转字符串II
-https://leetcode-cn.com/problems/reverse-string-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/reverse-string-ii/)
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
@@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/reverse-string-ii/
使用C++库函数reverse的版本如下:
-```C++
+```CPP
class Solution {
public:
string reverseStr(string s, int k) {
@@ -65,11 +65,11 @@ public:
};
```
-那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)道理是一样的。
+那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://programmercarl.com/0344.反转字符串.html)道理是一样的。
下面我实现的reverse函数区间是左闭右闭区间,代码如下:
-```C++
+```CPP
class Solution {
public:
void reverse(string& s, int start, int end) {
@@ -152,37 +152,58 @@ class Solution {
}
}
```
+```java
+// 解法3
+class Solution {
+ public String reverseStr(String s, int k) {
+ char[] ch = s.toCharArray();
+ // 1. 每隔 2k 个字符的前 k 个字符进行反转
+ for (int i = 0; i< ch.length; i += 2 * k) {
+ // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
+ if (i + k <= ch.length) {
+ reverse(ch, i, i + k -1);
+ continue;
+ }
+ // 3. 剩余字符少于 k 个,则将剩余字符全部反转
+ reverse(ch, i, ch.length - 1);
+ }
+ return new String(ch);
+ }
+ // 定义翻转函数
+ public void reverse(char[] ch, int i, int j) {
+ for (; i < j; i++, j--) {
+ char temp = ch[i];
+ ch[i] = ch[j];
+ ch[j] = temp;
+ }
+
+ }
+}
+```
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)
```
@@ -233,10 +254,32 @@ var reverseStr = function(s, k) {
```
+Swift:
+
+```swift
+func reverseStr(_ s: String, _ k: Int) -> String {
+ var ch = Array(s)
+
+ for i in stride(from: 0, to: ch.count, by: 2 * k) {
+ var left = i
+ var right = min(s.count - 1, left + k - 1)
+
+ while left < right {
+ (ch[left], ch[right]) = (ch[right], ch[left])
+ left += 1
+ right -= 1
+ }
+ }
+ return String(ch)
+}
+```
+
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0583.两个字符串的删除操作.md b/problems/0583.两个字符串的删除操作.md
index 9aef2310..91b07ca9 100644
--- a/problems/0583.两个字符串的删除操作.md
+++ b/problems/0583.两个字符串的删除操作.md
@@ -8,7 +8,7 @@
## 583. 两个字符串的删除操作
-题目链接:https://leetcode-cn.com/problems/delete-operation-for-two-strings/
+[力扣题目链接](https://leetcode-cn.com/problems/delete-operation-for-two-strings/)
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
@@ -20,7 +20,7 @@
## 思路
-本题和[动态规划:115.不同的子序列](https://mp.weixin.qq.com/s/1SULY2XVSROtk_hsoVLu8A)相比,其实就是两个字符串可以都可以删除了,情况虽说复杂一些,但整体思路是不变的。
+本题和[动态规划:115.不同的子序列](https://programmercarl.com/0115.不同的子序列.html)相比,其实就是两个字符串可以都可以删除了,情况虽说复杂一些,但整体思路是不变的。
这次是两个字符串可以相互删了,这种题目也知道用动态规划的思路来解,动规五部曲,分析如下:
@@ -56,7 +56,7 @@ dp[i][0]:word2为空字符串,以i-1为结尾的字符串word2要删除多
dp[0][j]的话同理,所以代码如下:
-```C++
+```CPP
vector> dp(word1.size() + 1, vector(word2.size() + 1));
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
@@ -78,7 +78,7 @@ for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
以上分析完毕,代码如下:
-```C++
+```CPP
class Solution {
public:
int minDistance(string word1, string word2) {
@@ -147,8 +147,38 @@ class Solution:
```
Go:
+```go
+func minDistance(word1 string, word2 string) int {
+ dp := make([][]int, len(word1)+1)
+ for i := 0; i < len(dp); i++ {
+ dp[i] = make([]int, len(word2)+1)
+ }
+ //初始化
+ for i := 0; i < len(dp); i++ {
+ dp[i][0] = i
+ }
+ for j := 0; j < len(dp[0]); j++ {
+ dp[0][j] = j
+ }
+ for i := 1; i < len(dp); i++ {
+ for j := 1; j < len(dp[i]); j++ {
+ if word1[i-1] == word2[j-1] {
+ dp[i][j] = dp[i-1][j-1]
+ } else {
+ dp[i][j] = min(min(dp[i-1][j]+1, dp[i][j-1]+1), dp[i-1][j-1]+2)
+ }
+ }
+ }
+ return dp[len(dp)-1][len(dp[0])-1]
+}
-
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+```
Javascript:
```javascript
const minDistance = (word1, word2) => {
@@ -181,4 +211,4 @@ const minDistance = (word1, word2) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md
index 19b58bd3..e21efcb3 100644
--- a/problems/0617.合并二叉树.md
+++ b/problems/0617.合并二叉树.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 617.合并二叉树
+# 617.合并二叉树
-题目地址:https://leetcode-cn.com/problems/merge-two-binary-trees/
+[力扣题目链接](https://leetcode-cn.com/problems/merge-two-binary-trees/)
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
@@ -21,7 +21,7 @@
注意: 合并必须从两个树的根节点开始。
-## 思路
+# 思路
相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?
@@ -90,7 +90,7 @@ return t1;
此时前序遍历,完整代码就写出来了,如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
@@ -107,7 +107,7 @@ public:
那么中序遍历也是可以的,代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
@@ -124,7 +124,7 @@ public:
后序遍历依然可以,代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
@@ -145,7 +145,7 @@ public:
不修改输入树的结构,前序遍历,代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
@@ -165,11 +165,11 @@ public:
使用迭代法,如何同时处理两棵树呢?
-思路我们在[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)中的迭代法已经讲过一次了,求二叉树对称的时候就是把两个树的节点同时加入队列进行比较。
+思路我们在[二叉树:我对称么?](https://programmercarl.com/0101.对称二叉树.html)中的迭代法已经讲过一次了,求二叉树对称的时候就是把两个树的节点同时加入队列进行比较。
本题我们也使用队列,模拟的层序遍历,代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
@@ -209,14 +209,14 @@ public:
};
```
-## 拓展
+# 拓展
当然也可以秀一波指针的操作,这是我写的野路子,大家就随便看看就行了,以防带跑遍了。
如下代码中,想要更改二叉树的值,应该传入指向指针的指针。
代码如下:(前序遍历)
-```C++
+```CPP
class Solution {
public:
void process(TreeNode** t1, TreeNode** t2) {
@@ -241,21 +241,21 @@ public:
};
```
-## 总结
+# 总结
合并二叉树,也是二叉树操作的经典题目,如果没有接触过的话,其实并不简单,因为我们习惯了操作一个二叉树,一起操作两个二叉树,还会有点懵懵的。
-这不是我们第一次操作两颗二叉树了,在[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)中也一起操作了两棵二叉树。
+这不是我们第一次操作两颗二叉树了,在[二叉树:我对称么?](https://programmercarl.com/0101.对称二叉树.html)中也一起操作了两棵二叉树。
迭代法中,一般一起操作两个树都是使用队列模拟类似层序遍历,同时处理两个树的节点,这种方式最好理解,如果用模拟递归的思路的话,要复杂一些。
最后拓展中,我给了一个操作指针的野路子,大家随便看看就行了,如果学习C++的话,可以在去研究研究。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -274,7 +274,7 @@ class Solution {
```Java
class Solution {
- // 迭代
+ // 使用栈迭代
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
@@ -310,8 +310,47 @@ class Solution {
}
}
```
+```java
+class Solution {
+ // 使用队列迭代
+ public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
+ if (root1 == null) return root2;
+ if (root2 ==null) return root1;
+ Queue queue = new LinkedList<>();
+ queue.offer(root1);
+ queue.offer(root2);
+ while (!queue.isEmpty()) {
+ TreeNode node1 = queue.poll();
+ TreeNode node2 = queue.poll();
+ // 此时两个节点一定不为空,val相加
+ node1.val = node1.val + node2.val;
+ // 如果两棵树左节点都不为空,加入队列
+ if (node1.left != null && node2.left != null) {
+ queue.offer(node1.left);
+ queue.offer(node2.left);
+ }
+ // 如果两棵树右节点都不为空,加入队列
+ if (node1.right != null && node2.right != null) {
+ queue.offer(node1.right);
+ queue.offer(node2.right);
+ }
+ // 若node1的左节点为空,直接赋值
+ if (node1.left == null && node2.left != null) {
+ node1.left = node2.left;
+ }
+ // 若node2的左节点为空,直接赋值
+ if (node1.right == null && node2.right != null) {
+ node1.right = node2.right;
+ }
+ }
+ return root1;
+ }
+}
+```
-Python:
+## Python
+
+**递归法 - 前序遍历**
```python
# Definition for a binary tree node.
# class TreeNode:
@@ -319,44 +358,60 @@ Python:
# self.val = val
# self.left = left
# self.right = right
-# 递归法*前序遍历
class Solution:
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
- if not root1: return root2 // 如果t1为空,合并之后就应该是t2
- if not root2: return root1 // 如果t2为空,合并之后就应该是t1
- root1.val = root1.val + root2.val //中
- root1.left = self.mergeTrees(root1.left , root2.left) //左
- root1.right = self.mergeTrees(root1.right , root2.right) //右
- return root1 //root1修改了结构和数值
+ # 递归终止条件:
+ # 但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None.
+ if not root1:
+ return root2
+ if not root2:
+ return root1
+ # 上面的递归终止条件保证了代码执行到这里root1, root2都非空.
+ root1.val += root2.val # 中
+ root1.left = self.mergeTrees(root1.left, root2.left) #左
+ root1.right = self.mergeTrees(root1.right, root2.right) # 右
+
+ return root1 # ⚠️ 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间.
-# 迭代法-覆盖原来的树
-class Solution:
- def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
- if not root1: return root2
- if not root2: return root1
- # 迭代,将树2覆盖到树1
- queue1 = [root1]
- queue2 = [root2]
- root = root1
- while queue1 and queue2:
- root1 = queue1.pop(0)
- root2 = queue2.pop(0)
- root1.val += root2.val
- if not root1.left: # 如果树1左儿子不存在,则覆盖后树1的左儿子为树2的左儿子
- root1.left = root2.left
- elif root1.left and root2.left:
- queue1.append(root1.left)
- queue2.append(root2.left)
-
- if not root1.right: # 同理,处理右儿子
- root1.right = root2.right
- elif root1.right and root2.right:
- queue1.append(root1.right)
- queue2.append(root2.right)
- return root
```
-Go:
+**迭代法**
+```python
+class Solution:
+ def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
+ if not root1:
+ return root2
+ if not root2:
+ return root1
+
+ queue = deque()
+ queue.append(root1)
+ queue.append(root2)
+
+ while queue:
+ node1 = queue.popleft()
+ node2 = queue.popleft()
+ # 更新queue
+ # 只有两个节点都有左节点时, 再往queue里面放.
+ if node1.left and node2.left:
+ queue.append(node1.left)
+ queue.append(node2.left)
+ # 只有两个节点都有右节点时, 再往queue里面放.
+ if node1.right and node2.right:
+ queue.append(node1.right)
+ queue.append(node2.right)
+
+ # 更新当前节点. 同时改变当前节点的左右孩子.
+ node1.val += node2.val
+ if not node1.left and node2.left:
+ node1.left = node2.left
+ if not node1.right and node2.right:
+ node1.right = node2.right
+
+ return root1
+```
+
+## Go
```go
/**
@@ -408,9 +463,49 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
root1.Right = mergeTrees(root1.Right, root2.Right)
return root1
}
+
+// 迭代版本
+func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
+ queue := make([]*TreeNode,0)
+ if root1 == nil{
+ return root2
+ }
+ if root2 == nil{
+ return root1
+ }
+ queue = append(queue,root1)
+ queue = append(queue,root2)
+
+ for size:=len(queue);size>0;size=len(queue){
+ node1 := queue[0]
+ queue = queue[1:]
+ node2 := queue[0]
+ queue = queue[1:]
+ node1.Val += node2.Val
+ // 左子树都不为空
+ if node1.Left != nil && node2.Left != nil{
+ queue = append(queue,node1.Left)
+ queue = append(queue,node2.Left)
+ }
+ // 右子树都不为空
+ if node1.Right !=nil && node2.Right !=nil{
+ queue = append(queue,node1.Right)
+ queue = append(queue,node2.Right)
+ }
+ // 树 1 的左子树为 nil,直接接上树 2 的左子树
+ if node1.Left == nil{
+ node1.Left = node2.Left
+ }
+ // 树 1 的右子树为 nil,直接接上树 2 的右子树
+ if node1.Right == nil{
+ node1.Right = node2.Right
+ }
+ }
+ return root1
+}
```
-JavaScript:
+## JavaScript
```javascript
/**
@@ -446,4 +541,4 @@ var mergeTrees = function (root1, root2) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0647.回文子串.md b/problems/0647.回文子串.md
index d3b734ae..7b90ca0c 100644
--- a/problems/0647.回文子串.md
+++ b/problems/0647.回文子串.md
@@ -8,7 +8,7 @@
## 647. 回文子串
-题目链接:https://leetcode-cn.com/problems/palindromic-substrings/
+[力扣题目链接](https://leetcode-cn.com/problems/palindromic-substrings/)
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
@@ -61,7 +61,7 @@
以上三种情况分析完了,那么递归公式如下:
-```C++
+```CPP
if (s[i] == s[j]) {
if (j - i <= 1) { // 情况一 和 情况二
result++;
@@ -101,7 +101,7 @@ dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图:
代码如下:
-```C++
+```CPP
for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序
for (int j = i; j < s.size(); j++) {
if (s[i] == s[j]) {
@@ -129,7 +129,7 @@ for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int countSubstrings(string s) {
@@ -154,7 +154,7 @@ public:
```
以上代码是为了凸显情况一二三,当然是可以简洁一下的,如下:
-```C++
+```CPP
class Solution {
public:
int countSubstrings(string s) {
@@ -192,7 +192,7 @@ public:
**这两种情况可以放在一起计算,但分别计算思路更清晰,我倾向于分别计算**,代码如下:
-```C++
+```CPP
class Solution {
public:
int countSubstrings(string s) {
@@ -412,4 +412,4 @@ const countSubstrings = (s) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0649.Dota2参议院.md b/problems/0649.Dota2参议院.md
index 696124d6..e2900824 100644
--- a/problems/0649.Dota2参议院.md
+++ b/problems/0649.Dota2参议院.md
@@ -10,6 +10,9 @@
# 649. Dota2 参议院
+[力扣题目链接](https://leetcode-cn.com/problems/dota2-senate/)
+
+
Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇)
Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中,每一位参议员都可以行使两项权利中的一项:
@@ -67,7 +70,7 @@ Dota2 参议院由来自两派的参议员组成。现在参议院希望对一
局部最优可以退出全局最优,举不出反例,那么试试贪心。
-如果对贪心算法理论基础还不了解的话,可以看看这篇:[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/O935TaoHE9Eexwe_vSbRAg) ,相信看完之后对贪心就有基本的了解了。
+如果对贪心算法理论基础还不了解的话,可以看看这篇:[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html) ,相信看完之后对贪心就有基本的了解了。
# 代码实现
@@ -78,7 +81,7 @@ Dota2 参议院由来自两派的参议员组成。现在参议院希望对一
C++代码如下:
-```C++
+```CPP
class Solution {
public:
string predictPartyVictory(string senate) {
@@ -115,16 +118,102 @@ public:
## Java
```java
+class Solution {
+ public String predictPartyVictory(String senateStr) {
+ // R = true表示本轮循环结束后,字符串里依然有R。D同理
+ Boolean R = true, D = true;
+ // 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
+ int flag = 0;
+ byte[] senate = senateStr.getBytes();
+ while (R && D) { // 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
+ R = false;
+ D = false;
+ for (int i = 0; i < senate.length; i++) {
+ if (senate[i] == 'R') {
+ if (flag < 0) senate[i] = 0; // 消灭R,R此时为false
+ else R = true; // 如果没被消灭,本轮循环结束有R
+ flag++;
+ }
+ if (senate[i] == 'D') {
+ if (flag > 0) senate[i] = 0;
+ else D = true;
+ flag--;
+ }
+ }
+ }
+ // 循环结束之后,R和D只能有一个为true
+ return R == true ? "Radiant" : "Dire";
+ }
+}
```
## Python
```python
+class Solution:
+ def predictPartyVictory(self, senate: str) -> str:
+ # R = true表示本轮循环结束后,字符串里依然有R。D同理
+ R , D = True, True
+
+ # 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
+ flag = 0
+
+ senate = list(senate)
+ while R and D: # 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
+ R = False
+ D = False
+ for i in range(len(senate)) :
+ if senate[i] == 'R' :
+ if flag < 0: senate[i] = '0' # 消灭R,R此时为false
+ else: R = True # 如果没被消灭,本轮循环结束有R
+ flag += 1
+ if senate[i] == 'D':
+ if flag > 0: senate[i] = '0'
+ else: D = True
+ flag -= 1
+ # 循环结束之后,R和D只能有一个为true
+ return "Radiant" if R else "Dire"
```
## Go
```go
+
+func predictPartyVictory(senateStr string) string {
+ // R = true表示本轮循环结束后,字符串里依然有R。D同理
+ R, D := true, true
+ // 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
+ flag := 0
+
+ senate := []byte(senateStr)
+ for R && D { // 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
+ R = false
+ D = false
+ for i := 0; i < len(senate); i++ {
+ if senate[i] == 'R' {
+ if flag < 0 {
+ senate[i] = 0 // 消灭R,R此时为false
+ } else {
+ R = true // 如果没被消灭,本轮循环结束有R
+ }
+ flag++;
+ }
+ if (senate[i] == 'D') {
+ if flag > 0 {
+ senate[i] = 0
+ } else {
+ D = true
+ }
+ flag--
+ }
+ }
+ }
+ // 循环结束之后,R和D只能有一个为true
+ if R {
+ return "Radiant"
+ }
+ return "Dire";
+}
```
## JavaScript
@@ -136,5 +225,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md
index 4e1e7a72..b644a0a3 100644
--- a/problems/0654.最大二叉树.md
+++ b/problems/0654.最大二叉树.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 654.最大二叉树
+# 654.最大二叉树
-题目地址:https://leetcode-cn.com/problems/maximum-binary-tree/
+[力扣题目地址](https://leetcode-cn.com/problems/maximum-binary-tree/)
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
@@ -27,7 +27,7 @@
给定的数组的大小在 [1, 1000] 之间。
-## 思路
+# 思路
最大二叉树的构建过程如下:
@@ -41,7 +41,7 @@
代码如下:
-```
+```CPP
TreeNode* constructMaximumBinaryTree(vector& nums)
```
@@ -53,7 +53,7 @@ TreeNode* constructMaximumBinaryTree(vector& nums)
代码如下:
-```
+```CPP
TreeNode* node = new TreeNode(0);
if (nums.size() == 1) {
node->val = nums[0];
@@ -68,7 +68,7 @@ if (nums.size() == 1) {
1. 先要找到数组中最大的值和对应的下表, 最大的值构造根节点,下表用来下一步分割数组。
代码如下:
-```
+```CPP
int maxValue = 0;
int maxValueIndex = 0;
for (int i = 0; i < nums.size(); i++) {
@@ -86,7 +86,7 @@ node->val = maxValue;
这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值。
代码如下:
-```
+```CPP
if (maxValueIndex > 0) {
vector newVec(nums.begin(), nums.begin() + maxValueIndex);
node->left = constructMaximumBinaryTree(newVec);
@@ -99,7 +99,7 @@ if (maxValueIndex > 0) {
代码如下:
-```
+```CPP
if (maxValueIndex < (nums.size() - 1)) {
vector newVec(nums.begin() + maxValueIndex + 1, nums.end());
node->right = constructMaximumBinaryTree(newVec);
@@ -107,7 +107,7 @@ if (maxValueIndex < (nums.size() - 1)) {
```
这样我们就分析完了,整体代码如下:(详细注释)
-```C++
+```CPP
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector& nums) {
@@ -143,11 +143,11 @@ public:
以上代码比较冗余,效率也不高,每次还要切割的时候每次都要定义新的vector(也就是数组),但逻辑比较清晰。
-和文章[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)中一样的优化思路,就是每次分隔不用定义新的数组,而是通过下表索引直接在原数组上操作。
+和文章[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html)中一样的优化思路,就是每次分隔不用定义新的数组,而是通过下表索引直接在原数组上操作。
优化后代码如下:
-```C++
+```CPP
class Solution {
private:
// 在左闭右开区间[left, right),构造二叉树
@@ -177,12 +177,12 @@ public:
};
```
-## 拓展
+# 拓展
可以发现上面的代码看上去简洁一些,**主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空**
第一版递归过程:(加了if判断,为了不让空节点进入递归)
-```C++
+```CPP
if (maxValueIndex > 0) { // 这里加了判断是为了不让空节点进入递归
vector newVec(nums.begin(), nums.begin() + maxValueIndex);
@@ -209,10 +209,10 @@ root->right = traversal(nums, maxValueIndex + 1, right);
第二版相应的终止条件,是遇到空节点,也就是数组区间为0,就终止了。
+# 总结
-## 总结
-这道题目其实和 [二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 是一个思路,比[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 还简单一些。
+这道题目其实和 [二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 是一个思路,比[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 还简单一些。
**注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下表索引直接在原数组上操作,这样可以节约时间和空间上的开销。**
@@ -220,10 +220,10 @@ root->right = traversal(nums, maxValueIndex + 1, right);
其实就是不同代码风格的实现,**一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。**
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -255,30 +255,38 @@ class Solution {
}
```
-Python:
+## Python
+
```python
-# 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 constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
- if not nums: return None //终止条件
- root = TreeNode(max(nums)) //新建节点
- p = nums.index(root.val) //找到最大值位置
- if p > 0: //保证有左子树
- root.left = self.constructMaximumBinaryTree(nums[:p]) //递归
- if p < len(nums): //保证有右子树
- root.right = self.constructMaximumBinaryTree(nums[p+1:]) //递归
+ return self.traversal(nums, 0, len(nums))
+
+ def traversal(self, nums: List[int], begin: int, end: int) -> TreeNode:
+ # 列表长度为0时返回空节点
+ if begin == end:
+ return None
+
+ # 找到最大的值和其对应的下标
+ max_index = begin
+ for i in range(begin, end):
+ if nums[i] > nums[max_index]:
+ max_index = i
+
+ # 构建当前节点
+ root = TreeNode(nums[max_index])
+
+ # 递归构建左右子树
+ root.left = self.traversal(nums, begin, max_index)
+ root.right = self.traversal(nums, max_index + 1, end)
+
return root
```
-Go:
+## Go
-> 654. 最大二叉树
```go
/**
@@ -311,7 +319,7 @@ func findMax(nums []int) (index int){
}
```
-JavaScript版本
+## JavaScript
```javascript
/**
@@ -354,4 +362,4 @@ var constructMaximumBinaryTree = function (nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0657.机器人能否返回原点.md b/problems/0657.机器人能否返回原点.md
index cd26836c..ffa5d6f2 100644
--- a/problems/0657.机器人能否返回原点.md
+++ b/problems/0657.机器人能否返回原点.md
@@ -9,7 +9,7 @@
# 657. 机器人能否返回原点
-题目地址:https://leetcode-cn.com/problems/robot-return-to-origin/
+[力扣题目链接](https://leetcode-cn.com/problems/robot-return-to-origin/)
在二维平面上,有一个机器人从原点 (0, 0) 开始。给出它的移动顺序,判断这个机器人在完成移动后是否在 (0, 0) 处结束。
@@ -48,7 +48,7 @@
C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool judgeCircle(string moves) {
@@ -71,26 +71,89 @@ public:
## Java
```java
+// 时间复杂度:O(n)
+// 空间复杂度:如果采用 toCharArray,则是 O(n);如果使用 charAt,则是 O(1)
+class Solution {
+ public boolean judgeCircle(String moves) {
+ int x = 0;
+ int y = 0;
+ for (char c : moves.toCharArray()) {
+ if (c == 'U') y++;
+ if (c == 'D') y--;
+ if (c == 'L') x++;
+ if (c == 'R') x--;
+ }
+ return x == 0 && y == 0;
+ }
+}
```
## Python
```python
+# 时间复杂度:O(n)
+# 空间复杂度:O(1)
+class Solution:
+ def judgeCircle(self, moves: str) -> bool:
+ x = 0 # 记录当前位置
+ y = 0
+ for i in range(len(moves)):
+ if (moves[i] == 'U'):
+ y += 1
+ if (moves[i] == 'D'):
+ y -= 1
+ if (moves[i] == 'L'):
+ x += 1
+ if (moves[i] == 'R'):
+ x -= 1
+ return x == 0 and y == 0
```
## Go
```go
+func judgeCircle(moves string) bool {
+ x := 0
+ y := 0
+ for i := 0; i < len(moves); i++ {
+ if moves[i] == 'U' {
+ y++
+ }
+ if moves[i] == 'D' {
+ y--
+ }
+ if moves[i] == 'L' {
+ x++
+ }
+ if moves[i] == 'R' {
+ x--
+ }
+ }
+ return x == 0 && y == 0;
+}
```
## JavaScript
```js
+// 时间复杂度:O(n)
+// 空间复杂度:O(1)
+var judgeCircle = function(moves) {
+ var x = 0; // 记录当前位置
+ var y = 0;
+ for (var i = 0; i < moves.length; i++) {
+ if (moves[i] == 'U') y++;
+ if (moves[i] == 'D') y--;
+ if (moves[i] == 'L') x++;
+ if (moves[i] == 'R') x--;
+ }
+ return x == 0 && y == 0;
+};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md
index 26394eaa..ef6a3e00 100644
--- a/problems/0669.修剪二叉搜索树.md
+++ b/problems/0669.修剪二叉搜索树.md
@@ -10,9 +10,9 @@
> 如果不对递归有深刻的理解,本题有点难
> 单纯移除一个节点那还不够,要修剪!
-## 669. 修剪二叉搜索树
+# 669. 修剪二叉搜索树
-题目链接:https://leetcode-cn.com/problems/trim-a-binary-search-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/trim-a-binary-search-tree/)
给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
@@ -20,7 +20,7 @@

-## 思路
+# 思路
相信看到这道题目大家都感觉是一道简单题(事实上leetcode上也标明是简单)。
@@ -32,7 +32,7 @@
不难写出如下代码:
-```C++
+```CPP
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
@@ -71,7 +71,7 @@ public:
但是有返回值,更方便,可以通过递归函数的返回值来移除节点。
-这样的做法在[二叉树:搜索树中的插入操作](https://mp.weixin.qq.com/s/lwKkLQcfbCNX2W-5SOeZEA)和[二叉树:搜索树中的删除操作](https://mp.weixin.qq.com/s/-p-Txvch1FFk3ygKLjPAKw)中大家已经了解过了。
+这样的做法在[二叉树:搜索树中的插入操作](https://programmercarl.com/0701.二叉搜索树中的插入操作.html)和[二叉树:搜索树中的删除操作](https://programmercarl.com/0450.删除二叉搜索树中的节点.html)中大家已经了解过了。
代码如下:
@@ -145,7 +145,7 @@ root->left = trimBST(root->left, low, high);
最后整体代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
@@ -167,7 +167,7 @@ public:
精简之后代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
@@ -195,7 +195,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int L, int R) {
@@ -228,7 +228,7 @@ public:
};
```
-## 总结
+# 总结
修剪二叉搜索树其实并不难,但在递归法中大家可看出我费了很大的功夫来讲解如何删除节点的,这个思路其实是比较绕的。
@@ -238,10 +238,10 @@ public:
本题我依然给出递归法和迭代法,初学者掌握递归就可以了,如果想进一步学习,就把迭代法也写一写。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -264,16 +264,10 @@ class Solution {
```
-Python:
+## Python
```python3
-# 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 trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
if not root: return root
@@ -285,17 +279,12 @@ class Solution:
root.right = self.trimBST(root.right,low,high) // root->right接入符合条件的右孩子
return root
```
-Go:
+
+## Go
+
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
+// 递归
func trimBST(root *TreeNode, low int, high int) *TreeNode {
if root==nil{
return nil
@@ -312,10 +301,43 @@ func trimBST(root *TreeNode, low int, high int) *TreeNode {
root.Right=trimBST(root.Right,low,high)
return root
}
+
+// 迭代
+func trimBST(root *TreeNode, low int, high int) *TreeNode {
+ if root == nil {
+ return nil
+ }
+ // 处理 root,让 root 移动到[low, high] 范围内,注意是左闭右闭
+ for root != nil && (root.Valhigh){
+ if root.Val < low{
+ root = root.Right
+ }else{
+ root = root.Left
+ }
+ }
+ cur := root
+ // 此时 root 已经在[low, high] 范围内,处理左孩子元素小于 low 的情况(左节点是一定小于 root.Val,因此天然小于 high)
+ for cur != nil{
+ for cur.Left!=nil && cur.Left.Val < low{
+ cur.Left = cur.Left.Right
+ }
+ cur = cur.Left
+ }
+ cur = root
+ // 此时 root 已经在[low, high] 范围内,处理右孩子大于 high 的情况
+ for cur != nil{
+ for cur.Right!=nil && cur.Right.Val > high{
+ cur.Right = cur.Right.Left
+ }
+ cur = cur.Right
+ }
+ return root
+}
```
-JavaScript版本:
+## JavaScript版本
+
迭代:
```js
var trimBST = function(root, low, high) {
@@ -373,4 +395,4 @@ var trimBST = function (root,low,high) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0673.最长递增子序列的个数.md b/problems/0673.最长递增子序列的个数.md
index da0be740..b3907e0e 100644
--- a/problems/0673.最长递增子序列的个数.md
+++ b/problems/0673.最长递增子序列的个数.md
@@ -9,6 +9,10 @@
# 673.最长递增子序列的个数
+
+[力扣题目链接](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/)
+
+
给定一个未排序的整数数组,找到最长递增子序列的个数。
示例 1:
@@ -58,7 +62,7 @@ if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
代码如下:
-```C++
+```CPP
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
count[i] = count[j];
@@ -71,7 +75,7 @@ if (nums[i] > nums[j]) {
当然也可以这么写:
-```C++
+```CPP
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1; // 更新dp[i]放在这里,就不用max了
@@ -88,7 +92,7 @@ if (nums[i] > nums[j]) {
代码如下:
-```C++
+```CPP
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
@@ -131,7 +135,7 @@ dp[i] 是由0到i-1各个位置的最长升序子序列 推导而来,那么遍
j其实就是0到i-1,遍历i的循环里外层,遍历j则在内层,代码如下:
-```C++
+```CPP
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
@@ -152,7 +156,7 @@ for (int i = 1; i < nums.size(); i++) {
代码如下:
-```C++
+```CPP
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
@@ -184,7 +188,7 @@ for (int i = 0; i < nums.size(); i++) {
以上分析完毕,C++整体代码如下:
-```C++
+```CPP
class Solution {
public:
int findNumberOfLIS(vector& nums) {
@@ -224,16 +228,110 @@ public:
## Java
```java
+class Solution {
+ public int findNumberOfLIS(int[] nums) {
+ if (nums.length <= 1) return nums.length;
+ int[] dp = new int[nums.length];
+ for(int i = 0; i < dp.length; i++) dp[i] = 1;
+ int[] count = new int[nums.length];
+ for(int i = 0; i < count.length; i++) count[i] = 1;
+
+ int maxCount = 0;
+ for (int i = 1; i < nums.length; i++) {
+ for (int j = 0; j < i; j++) {
+ if (nums[i] > nums[j]) {
+ if (dp[j] + 1 > dp[i]) {
+ dp[i] = dp[j] + 1;
+ count[i] = count[j];
+ } else if (dp[j] + 1 == dp[i]) {
+ count[i] += count[j];
+ }
+ }
+ if (dp[i] > maxCount) maxCount = dp[i];
+ }
+ }
+ int result = 0;
+ for (int i = 0; i < nums.length; i++) {
+ if (maxCount == dp[i]) result += count[i];
+ }
+ return result;
+ }
+}
```
## Python
```python
+class Solution:
+ def findNumberOfLIS(self, nums: List[int]) -> int:
+ size = len(nums)
+ if size<= 1: return size
+
+ dp = [1 for i in range(size)]
+ count = [1 for i in range(size)]
+
+ maxCount = 0
+ for i in range(1, size):
+ for j in range(i):
+ if nums[i] > nums[j]:
+ if dp[j] + 1 > dp[i] :
+ dp[i] = dp[j] + 1
+ count[i] = count[j]
+ elif dp[j] + 1 == dp[i] :
+ count[i] += count[j]
+ if dp[i] > maxCount:
+ maxCount = dp[i];
+ result = 0
+ for i in range(size):
+ if maxCount == dp[i]:
+ result += count[i]
+ return result;
```
## Go
```go
+
+func findNumberOfLIS(nums []int) int {
+ size := len(nums)
+ if size <= 1 {
+ return size
+ }
+
+ dp := make([]int, size);
+ for i, _ := range dp {
+ dp[i] = 1
+ }
+ count := make([]int, size);
+ for i, _ := range count {
+ count[i] = 1
+ }
+
+ maxCount := 0
+ for i := 1; i < size; i++ {
+ for j := 0; j < i; j++ {
+ if nums[i] > nums[j] {
+ if dp[j] + 1 > dp[i] {
+ dp[i] = dp[j] + 1
+ count[i] = count[j]
+ } else if dp[j] + 1 == dp[i] {
+ count[i] += count[j]
+ }
+ }
+ if dp[i] > maxCount {
+ maxCount = dp[i]
+ }
+ }
+ }
+
+ result := 0
+ for i := 0; i < size; i++ {
+ if maxCount == dp[i] {
+ result += count[i]
+ }
+ }
+ return result
+}
```
## JavaScript
@@ -245,5 +343,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md
index 614bff72..3f3b5e6f 100644
--- a/problems/0674.最长连续递增序列.md
+++ b/problems/0674.最长连续递增序列.md
@@ -8,7 +8,7 @@
## 674. 最长连续递增序列
-题目链接:https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/
+[力扣题目链接](https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/)
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
@@ -24,7 +24,7 @@
输入:nums = [2,2,2,2,2]
输出:1
解释:最长连续递增序列是 [2], 长度为1。
-
+
提示:
* 0 <= nums.length <= 10^4
@@ -33,7 +33,7 @@
## 思路
-本题相对于昨天的[动态规划:300.最长递增子序列](https://mp.weixin.qq.com/s/f8nLO3JGfgriXep_gJQpqQ)最大的区别在于“连续”。
+本题相对于昨天的[动态规划:300.最长递增子序列](https://programmercarl.com/0300.最长上升子序列.html)最大的区别在于“连续”。
本题要求的是最长**连续**递增序列
@@ -53,7 +53,7 @@
即:dp[i + 1] = dp[i] + 1;
-**注意这里就体现出和[动态规划:300.最长递增子序列](https://mp.weixin.qq.com/s/f8nLO3JGfgriXep_gJQpqQ)的区别!**
+**注意这里就体现出和[动态规划:300.最长递增子序列](https://programmercarl.com/0300.最长上升子序列.html)的区别!**
因为本题要求连续递增子序列,所以就必要比较nums[i + 1]与nums[i],而不用去比较nums[j]与nums[i] (j是在0到i之间遍历)。
@@ -73,7 +73,7 @@
本文在确定递推公式的时候也说明了为什么本题只需要一层for循环,代码如下:
-```C++
+```CPP
for (int i = 0; i < nums.size() - 1; i++) {
if (nums[i + 1] > nums[i]) { // 连续记录
dp[i + 1] = dp[i] + 1; // 递推公式
@@ -91,7 +91,7 @@ for (int i = 0; i < nums.size() - 1; i++) {
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int findLengthOfLCIS(vector& nums) {
@@ -118,7 +118,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
int findLengthOfLCIS(vector& nums) {
@@ -144,7 +144,7 @@ public:
本题也是动规里子序列问题的经典题目,但也可以用贪心来做,大家也会发现贪心好像更简单一点,而且空间复杂度仅是O(1)。
-在动规分析中,关键是要理解和[动态规划:300.最长递增子序列](https://mp.weixin.qq.com/s/f8nLO3JGfgriXep_gJQpqQ)的区别。
+在动规分析中,关键是要理解和[动态规划:300.最长递增子序列](https://programmercarl.com/0300.最长上升子序列.html)的区别。
**要联动起来,才能理解递增子序列怎么求,递增连续子序列又要怎么求**。
@@ -268,4 +268,4 @@ const findLengthOfLCIS = (nums) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0684.冗余连接.md b/problems/0684.冗余连接.md
index 9215af5f..c9eb33c4 100644
--- a/problems/0684.冗余连接.md
+++ b/problems/0684.冗余连接.md
@@ -37,7 +37,7 @@
这里整理出我的并查集模板如下:
-```C++
+```CPP
int n = 1005; // 节点数量3 到 1000
int father[1005];
@@ -88,7 +88,7 @@ bool same(int u, int v) {
并查集C++代码如下:
-```C++
+```CPP
class Solution {
private:
int n = 1005; // 节点数量3 到 1000
@@ -161,7 +161,6 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
@@ -169,3 +168,4 @@ public:
+
diff --git a/problems/0685.冗余连接II.md b/problems/0685.冗余连接II.md
index b744c831..68d0376e 100644
--- a/problems/0685.冗余连接II.md
+++ b/problems/0685.冗余连接II.md
@@ -11,7 +11,7 @@
# 685.冗余连接II
-题目地址:https://leetcode-cn.com/problems/redundant-connection-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/redundant-connection-ii/)
在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。
@@ -58,7 +58,7 @@
首先先计算节点的入度,代码如下:
-```C++
+```CPP
int inDegree[N] = {0}; // 记录节点入度
n = edges.size(); // 边的数量
for (int i = 0; i < n; i++) {
@@ -70,7 +70,7 @@ for (int i = 0; i < n; i++) {
代码如下:
-```C++
+```CPP
vector vec; // 记录入度为2的边(如果有的话就两条边)
// 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
for (int i = n - 1; i >= 0; i--) {
@@ -112,7 +112,7 @@ vector getRemoveEdge(const vector>& edges)
本题C++代码如下:(详细注释了)
-```C++
+```CPP
class Solution {
private:
static const int N = 1010; // 如题:二维数组大小的在3到1000范围内
@@ -224,6 +224,6 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md
index d6899ac5..c25ea12f 100644
--- a/problems/0700.二叉搜索树中的搜索.md
+++ b/problems/0700.二叉搜索树中的搜索.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 700.二叉搜索树中的搜索
+# 700.二叉搜索树中的搜索
-题目地址:https://leetcode-cn.com/problems/search-in-a-binary-search-tree/
+[力扣题目地址](https://leetcode-cn.com/problems/search-in-a-binary-search-tree/)
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
@@ -19,11 +19,11 @@
在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。
-## 思路
+# 思路
之前我们讲了都是普通二叉树,那么接下来看看二叉搜索树。
-在[关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A)中,我们已经讲过了二叉搜索树。
+在[关于二叉树,你该了解这些!](https://programmercarl.com/二叉树理论基础.html)中,我们已经讲过了二叉搜索树。
二叉搜索树是一个有序树:
@@ -73,13 +73,13 @@ return NULL;
这里可能会疑惑,在递归遍历的时候,什么时候直接return 递归函数的返回值,什么时候不用加这个 return呢。
-我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg)中讲了,如果要搜索一条边,递归函数就要加返回值,这里也是一样的道理。
+我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://programmercarl.com/0112.路径总和.html)中讲了,如果要搜索一条边,递归函数就要加返回值,这里也是一样的道理。
**因为搜索到目标节点了,就要立即return了,这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。**
整体代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
@@ -109,7 +109,7 @@ public:
所以迭代法代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
@@ -125,7 +125,7 @@ public:
第一次看到了如此简单的迭代法,是不是感动的痛哭流涕,哭一会~
-## 总结
+# 总结
本篇我们介绍了二叉搜索树的遍历方式,因为二叉搜索树的有序性,遍历的时候要比普通二叉树简单很多。
@@ -138,9 +138,9 @@ public:
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -207,22 +207,25 @@ class Solution {
}
```
-Python:
+## Python
递归法:
```python
-# 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 searchBST(self, root: TreeNode, val: int) -> TreeNode:
- if not root or root.val == val: return root //为空或者已经找到都是直接返回root,所以合并了
- if root.val > val: return self.searchBST(root.left,val) //注意一定要加return
- else: return self.searchBST(root.right,val)
+ # 为什么要有返回值:
+ # 因为搜索到目标节点就要立即return,
+ # 这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。
+
+ if not root or root.val == val:
+ return root
+
+ if root.val > val:
+ return self.searchBST(root.left, val)
+
+ if root.val < val:
+ return self.searchBST(root.right, val)
```
@@ -239,19 +242,11 @@ class Solution:
```
-Go:
+## Go
-> 递归法
+递归法:
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
//递归法
func searchBST(root *TreeNode, val int) *TreeNode {
if root==nil||root.Val==val{
@@ -264,17 +259,9 @@ func searchBST(root *TreeNode, val int) *TreeNode {
}
```
-> 迭代法
+迭代法:
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
//迭代法
func searchBST(root *TreeNode, val int) *TreeNode {
for root!=nil{
@@ -290,9 +277,9 @@ func searchBST(root *TreeNode, val int) *TreeNode {
}
```
-JavaScript版本
+## JavaScript
-> 递归
+递归:
```javascript
/**
@@ -318,9 +305,9 @@ var searchBST = function (root, val) {
return searchBST(root.right, val);
return null;
};
-```
+```
-> 迭代
+迭代:
```javascript
/**
@@ -355,4 +342,4 @@ var searchBST = function (root, val) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md
index 61027453..d5b9eb3f 100644
--- a/problems/0701.二叉搜索树中的插入操作.md
+++ b/problems/0701.二叉搜索树中的插入操作.md
@@ -7,9 +7,9 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 701.二叉搜索树中的插入操作
+# 701.二叉搜索树中的插入操作
-链接:https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/)
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据保证,新值和原始二叉搜索树中的任意节点值都不同。
@@ -24,7 +24,7 @@
* -10^8 <= val <= 10^8
* 新值和原始二叉搜索树中的任意节点值都不同
-## 思路
+# 思路
其实这道题目其实是一道简单题目,**但是题目中的提示:有多种有效的插入方式,还可以重构二叉搜索树,一下子吓退了不少人**,瞬间感觉题目复杂了很多。
@@ -96,7 +96,7 @@ return root;
整体代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
@@ -126,7 +126,7 @@ void traversal(TreeNode* cur, int val)
代码如下:
-```C++
+```CPP
class Solution {
private:
TreeNode* parent;
@@ -164,15 +164,15 @@ public:
## 迭代
-再来看看迭代法,对二叉搜索树迭代写法不熟悉,可以看这篇:[二叉树:二叉搜索树登场!](https://mp.weixin.qq.com/s/vsKrWRlETxCVsiRr8v_hHg)
+再来看看迭代法,对二叉搜索树迭代写法不熟悉,可以看这篇:[二叉树:二叉搜索树登场!](https://programmercarl.com/0700.二叉搜索树中的搜索.html)
在迭代法遍历的过程中,需要记录一下当前遍历的节点的父节点,这样才能做插入节点的操作。
-在[二叉树:搜索树的最小绝对差](https://mp.weixin.qq.com/s/Hwzml6698uP3qQCC1ctUQQ)和[二叉树:我的众数是多少?](https://mp.weixin.qq.com/s/KSAr6OVQIMC-uZ8MEAnGHg)中,都是用了记录pre和cur两个指针的技巧,本题也是一样的。
+在[二叉树:搜索树的最小绝对差](https://programmercarl.com/0530.二叉搜索树的最小绝对差.html)和[二叉树:我的众数是多少?](https://programmercarl.com/0501.二叉搜索树中的众数.html)中,都是用了记录pre和cur两个指针的技巧,本题也是一样的。
代码如下:
-```C++
+```CPP
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
@@ -195,7 +195,7 @@ public:
};
```
-## 总结
+# 总结
首先在二叉搜索树中的插入操作,大家不用恐惧其重构搜索树,其实根本不用重构。
@@ -204,9 +204,10 @@ public:
最后依然给出了迭代的方法,迭代的方法就需要记录当前遍历节点的父节点了,这个和没有返回值的递归函数实现的代码逻辑是一样的。
-## 其他语言版本
+# 其他语言版本
+
+## Java
-Java:
```java
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
@@ -253,9 +254,9 @@ class Solution {
}
```
-Python:
+## Python
-递归法
+**递归法** - 有返回值
```python
class Solution:
@@ -267,10 +268,68 @@ class Solution:
if root.val > val:
root.left = self.insertIntoBST(root.left, val) # 递归创建左子树
return root
+```
+
+**递归法** - 无返回值
+```python
+class Solution:
+ def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
+ if not root:
+ return TreeNode(val)
+ parent = None
+ def __traverse(cur: TreeNode, val: int) -> None:
+ # 在函数运行的同时把新节点插入到该被插入的地方.
+ nonlocal parent
+ if not cur:
+ new_node = TreeNode(val)
+ if parent.val < val:
+ parent.right = new_node
+ else:
+ parent.left = new_node
+ return
+
+ parent = cur # 重点: parent的作用只有运行到上面if not cur:才会发挥出来.
+ if cur.val < val:
+ __traverse(cur.right, val)
+ else:
+ __traverse(cur.left, val)
+ return
+ __traverse(root, val)
+ return root
+```
+
+**迭代法**
+与无返回值的递归函数的思路大体一致
+```python
+class Solution:
+ def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
+ if not root:
+ return TreeNode(val)
+ parent = None
+ cur = root
+
+ # 用while循环不断地找新节点的parent
+ while cur:
+ if cur.val < val:
+ parent = cur
+ cur = cur.right
+ elif cur.val > val:
+ parent = cur
+ cur = cur.left
+
+ # 运行到这意味着已经跳出上面的while循环,
+ # 同时意味着新节点的parent已经被找到.
+ # parent已被找到, 新节点已经ready. 把两个节点黏在一起就好了.
+ if parent.val > val:
+ parent.left = TreeNode(val)
+ else:
+ parent.right = TreeNode(val)
+
+ return root
+
```
-
-Go:
+## Go
递归法
@@ -287,8 +346,10 @@ func insertIntoBST(root *TreeNode, val int) *TreeNode {
}
return root
}
-```
-迭代法
+```
+
+迭代法
+
```go
func insertIntoBST(root *TreeNode, val int) *TreeNode {
if root == nil {
@@ -314,9 +375,9 @@ func insertIntoBST(root *TreeNode, val int) *TreeNode {
}
```
-JavaScript版本
+## JavaScript
-> 有返回值的递归写法
+有返回值的递归写法
```javascript
/**
@@ -348,7 +409,7 @@ var insertIntoBST = function (root, val) {
};
```
-> 无返回值的递归
+无返回值的递归
```javascript
/**
@@ -388,7 +449,7 @@ var insertIntoBST = function (root, val) {
};
```
-> 迭代
+迭代
```javascript
/**
@@ -431,4 +492,4 @@ var insertIntoBST = function (root, val) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md
index cce471ae..f358d2be 100644
--- a/problems/0704.二分查找.md
+++ b/problems/0704.二分查找.md
@@ -9,7 +9,7 @@
## 704. 二分查找
-题目链接:https://leetcode-cn.com/problems/binary-search/
+[力扣题目链接](https://leetcode-cn.com/problems/binary-search/)
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
@@ -63,7 +63,7 @@
代码如下:(详细注释)
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -102,7 +102,7 @@ public:
代码如下:(详细注释)
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -139,7 +139,7 @@ public:
## 相关题目推荐
-* [35.搜索插入位置](./0035.搜索插入位置.md)
+* [35.搜索插入位置](https://programmercarl.com/0035.搜索插入位置.html)
* 34.在排序数组中查找元素的第一个和最后一个位置
* 69.x 的平方根
* 367.有效的完全平方数
@@ -235,8 +235,6 @@ class Solution:
return -1
```
-
-
**Go:**
(版本一)左闭右闭区间
@@ -279,7 +277,7 @@ func search(nums []int, target int) int {
}
```
-**javaScript:**
+**JavaScript:**
```js
@@ -316,12 +314,204 @@ var search = function(nums, target) {
```
+**Ruby:**
+```ruby
+# (版本一)左闭右闭区间
+def search(nums, target)
+ left, right = 0, nums.length - 1
+ while left <= right # 由于定义target在一个在左闭右闭的区间里,因此极限情况下存在left==right
+ middle = (left + right) / 2
+ if nums[middle] > target
+ right = middle - 1
+ elsif nums[middle] < target
+ left = middle + 1
+ else
+ return middle # return兼具返回与跳出循环的作用
+ end
+ end
+ -1
+end
+# (版本二)左闭右开区间
+
+def search(nums, target)
+ left, right = 0, nums.length
+ while left < right # 由于定义target在一个在左闭右开的区间里,因此极限情况下right=left+1
+ middle = (left + right) / 2
+ if nums[middle] > target
+ right = middle
+ elsif nums[middle] < target
+ left = middle + 1
+ else
+ return middle
+ end
+ end
+ -1
+end
+```
+
+**Swift:**
+
+```swift
+// (版本一)左闭右闭区间
+func search(nums: [Int], target: Int) -> Int {
+ // 1. 先定义区间。这里的区间是[left, right]
+ var left = 0
+ var right = nums.count - 1
+
+ while left <= right {// 因为taeget是在[left, right]中,包括两个边界值,所以这里的left == right是有意义的
+ // 2. 计算区间中间的下标(如果left、right都比较大的情况下,left + right就有可能会溢出)
+ // let middle = (left + right) / 2
+ // 防溢出:
+ let middle = left + (right - left) / 2
+
+ // 3. 判断
+ if target < nums[middle] {
+ // 当目标在区间左侧,就需要更新右边的边界值,新区间为[left, middle - 1]
+ right = middle - 1
+ } else if target > nums[middle] {
+ // 当目标在区间右侧,就需要更新左边的边界值,新区间为[middle + 1, right]
+ left = middle + 1
+ } else {
+ // 当目标就是在中间,则返回中间值的下标
+ return middle
+ }
+ }
+
+ // 如果找不到目标,则返回-1
+ return -1
+}
+
+// (版本二)左闭右开区间
+func search(nums: [Int], target: Int) -> Int {
+ var left = 0
+ var right = nums.count
+
+ while left < right {
+ let middle = left + ((right - left) >> 1)
+
+ if target < nums[middle] {
+ right = middle
+ } else if target > nums[middle] {
+ left = middle + 1
+ } else {
+ return middle
+ }
+ }
+
+ return -1
+}
+
+```
+
+**Rust:**
+
+```rust
+# (版本一)左闭右闭区间
+
+impl Solution {
+ pub fn search(nums: Vec, target: i32) -> i32 {
+ let mut left:usize = 0;
+ let mut right:usize = nums.len() - 1;
+ while left as i32 <= right as i32{
+ let mid = (left + right) / 2;
+ if nums[mid] < target {
+ left = mid + 1;
+ } else if nums[mid] > target {
+ right = mid - 1;
+ } else {
+ return mid as i32;
+ }
+ }
+ -1
+ }
+}
+
+# (版本二)左闭右开区间
+
+impl Solution {
+ pub fn search(nums: Vec, target: i32) -> i32 {
+ let mut left:usize = 0;
+ let mut right:usize = nums.len();
+ while left < right {
+ let mid = (left + right) / 2;
+ if nums[mid] < target {
+ left = mid + 1;
+ } else if nums[mid] > target {
+ right = mid;
+ } else {
+ return mid as i32;
+ }
+ }
+ -1
+ }
+}
+```
+
+**C:**
+```c
+int search(int* nums, int numsSize, int target){
+ int left = 0;
+ int right = numsSize-1;
+ int middle = 0;
+ //若left小于等于right,说明区间中元素不为0
+ while(left<=right) {
+ //更新查找下标middle的值
+ middle = (left+right)/2;
+ //此时target可能会在[left,middle-1]区间中
+ if(nums[middle] > target) {
+ right = middle-1;
+ }
+ //此时target可能会在[middle+1,right]区间中
+ else if(nums[middle] < target) {
+ left = middle+1;
+ }
+ //当前下标元素等于target值时,返回middle
+ else if(nums[middle] == target){
+ return middle;
+ }
+ }
+ //若未找到target元素,返回-1
+ return -1;
+}
+```
+
+**PHP:**
+```php
+// 左闭右闭区间
+class Solution {
+ /**
+ * @param Integer[] $nums
+ * @param Integer $target
+ * @return Integer
+ */
+ function search($nums, $target) {
+ if (count($nums) == 0) {
+ return -1;
+ }
+ $left = 0;
+ $right = count($nums) - 1;
+ while ($left <= $right) {
+ $mid = floor(($left + $right) / 2);
+ if ($nums[$mid] == $target) {
+ return $mid;
+ }
+ if ($nums[$mid] > $target) {
+ $right = $mid - 1;
+ }
+ else {
+ $left = $mid + 1;
+ }
+ }
+ return -1;
+ }
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md
index 8be28f88..e05165a9 100644
--- a/problems/0707.设计链表.md
+++ b/problems/0707.设计链表.md
@@ -11,7 +11,7 @@
# 707.设计链表
-https://leetcode-cn.com/problems/design-linked-list/
+[力扣题目链接](https://leetcode-cn.com/problems/design-linked-list/)
题意:
@@ -28,9 +28,9 @@ https://leetcode-cn.com/problems/design-linked-list/
# 思路
-如果对链表的基础知识还不太懂,可以看这篇文章:[关于链表,你该了解这些!](https://mp.weixin.qq.com/s/fDGMmLrW7ZHlzkzlf_dZkw)
+如果对链表的基础知识还不太懂,可以看这篇文章:[关于链表,你该了解这些!](https://programmercarl.com/链表理论基础.html)
-如果对链表的虚拟头结点不清楚,可以看这篇文章:[链表:听说用虚拟头节点会方便很多?](https://mp.weixin.qq.com/s/L5aanfALdLEwVWGvyXPDqA)
+如果对链表的虚拟头结点不清楚,可以看这篇文章:[链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)
删除链表节点:

@@ -56,7 +56,7 @@ https://leetcode-cn.com/problems/design-linked-list/
## 代码
-```C++
+```CPP
class MyLinkedList {
public:
// 定义链表节点结构体
@@ -154,7 +154,129 @@ private:
## 其他语言版本
+C:
+```C
+typedef struct {
+ int val;
+ struct MyLinkedList* next;
+}MyLinkedList;
+/** Initialize your data structure here. */
+
+MyLinkedList* myLinkedListCreate() {
+ //这个题必须用虚拟头指针,参数都是一级指针,头节点确定后没法改指向了!!!
+ MyLinkedList* head = (MyLinkedList *)malloc(sizeof (MyLinkedList));
+ head->next = NULL;
+ return head;
+}
+
+/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
+int myLinkedListGet(MyLinkedList* obj, int index) {
+ MyLinkedList *cur = obj->next;
+ for (int i = 0; cur != NULL; i++){
+ if (i == index){
+ return cur->val;
+ }
+ else{
+ cur = cur->next;
+ }
+ }
+ return -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. */
+void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
+ MyLinkedList *nhead = (MyLinkedList *)malloc(sizeof (MyLinkedList));
+ nhead->val = val;
+ nhead->next = obj->next;
+ obj->next = nhead;
+
+}
+
+/** Append a node of value val to the last element of the linked list. */
+void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
+ MyLinkedList *cur = obj;
+ while(cur->next != NULL){
+ cur = cur->next;
+ }
+ MyLinkedList *ntail = (MyLinkedList *)malloc(sizeof (MyLinkedList));
+ ntail->val = val;
+ ntail->next = NULL;
+ cur->next = ntail;
+}
+
+/** 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. */
+void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
+ if (index == 0){
+ myLinkedListAddAtHead(obj, val);
+ return;
+ }
+ MyLinkedList *cur = obj->next;
+ for (int i = 1 ;cur != NULL; i++){
+ if (i == index){
+ MyLinkedList* newnode = (MyLinkedList *)malloc(sizeof (MyLinkedList));
+ newnode->val = val;
+ newnode->next = cur->next;
+ cur->next = newnode;
+ return;
+ }
+ else{
+ cur = cur->next;
+ }
+ }
+}
+
+/** Delete the index-th node in the linked list, if the index is valid. */
+void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
+ if (index == 0){
+ MyLinkedList *tmp = obj->next;
+ if (tmp != NULL){
+ obj->next = tmp->next;
+ free(tmp)
+ }
+ return;
+ }
+ MyLinkedList *cur = obj->next;
+ for (int i = 1 ;cur != NULL && cur->next != NULL; i++){
+ if (i == index){
+ MyLinkedList *tmp = cur->next;
+ if (tmp != NULL) {
+ cur->next = tmp->next;
+ free(tmp);
+ }
+ return;
+ }
+ else{
+ cur = cur->next;
+ }
+ }
+
+}
+
+void myLinkedListFree(MyLinkedList* obj) {
+ while(obj != NULL){
+ MyLinkedList *tmp = obj;
+ obj = obj->next;
+ free(tmp);
+ }
+}
+
+/**
+ * Your MyLinkedList struct will be instantiated and called as such:
+ * MyLinkedList* obj = myLinkedListCreate();
+ * int param_1 = myLinkedListGet(obj, index);
+
+ * myLinkedListAddAtHead(obj, val);
+
+ * myLinkedListAddAtTail(obj, val);
+
+ * myLinkedListAddAtIndex(obj, index, val);
+
+ * myLinkedListDeleteAtIndex(obj, index);
+
+ * myLinkedListFree(obj);
+*/
+```
Java:
```Java
@@ -758,6 +880,155 @@ MyLinkedList.prototype.deleteAtIndex = function(index) {
* obj.deleteAtIndex(index)
*/
```
+Kotlin:
+```kotlin
+class MyLinkedList {
+
+ var next: ListNode? = null
+
+ var size: Int = 0
+
+ fun get(index: Int): Int {
+ if (index + 1 > size) return -1
+ var cur = this.next
+ for (i in 0 until index) {
+ cur = cur?.next
+ }
+ return cur?.`val` ?: -1
+ }
+
+ fun addAtHead(`val`: Int) {
+ val head = ListNode(`val`)
+ head.next = this.next
+ this.next = head
+ size++
+ }
+
+ fun addAtTail(`val`: Int) {
+ val pre = ListNode(0)
+ pre.next = this.next
+ var cur: ListNode? = pre
+ while (cur?.next != null) {
+ cur = cur.next
+ }
+ cur?.next = ListNode(`val`)
+ this.next = pre.next
+ size++
+ }
+
+ fun addAtIndex(index: Int, `val`: Int) {
+ if (index > size) return
+ val pre = ListNode(0)
+ pre.next = this.next
+ var cur:ListNode? = pre
+ for (i in 0 until index) {
+ cur = cur?.next
+ }
+ val temp = cur?.next
+ cur?.next = ListNode(`val`)
+ cur?.next?.next = temp
+ this.next = pre.next
+ size++
+ }
+
+ fun deleteAtIndex(index: Int) {
+ if (index + 1 > size) return
+ val pre = ListNode(0)
+ pre.next = this.next
+ var cur: ListNode? = pre
+ for (i in 0 until index) {
+ cur = cur?.next
+ }
+ val temp = cur?.next?.next
+ cur?.next?.next = null
+ cur?.next = temp
+ this.next = pre.next
+ size--
+ }
+}
+```
+
+
+Swift:
+
+```swift
+class MyLinkedList {
+ var dummyHead: ListNode?
+ 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..
+
diff --git a/problems/0714.买卖股票的最佳时机含手续费.md b/problems/0714.买卖股票的最佳时机含手续费.md
index 2e8f9208..4ac4684e 100644
--- a/problems/0714.买卖股票的最佳时机含手续费.md
+++ b/problems/0714.买卖股票的最佳时机含手续费.md
@@ -9,7 +9,7 @@
## 714. 买卖股票的最佳时机含手续费
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
@@ -37,11 +37,11 @@
## 思路
-本题相对于[贪心算法:122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/VsTFA6U96l18Wntjcg3fcg),多添加了一个条件就是手续费。
+本题相对于[贪心算法:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),多添加了一个条件就是手续费。
## 贪心算法
-在[贪心算法:122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/VsTFA6U96l18Wntjcg3fcg)中使用贪心策略不用关心具体什么时候买卖,只要收集每天的正利润,最后稳稳的就是最大利润了。
+在[贪心算法:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html)中使用贪心策略不用关心具体什么时候买卖,只要收集每天的正利润,最后稳稳的就是最大利润了。
而本题有了手续费,就要关系什么时候买卖了,因为计算所获得利润,需要考虑买卖利润可能不足以手续费的情况。
@@ -60,7 +60,7 @@
贪心算法C++代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices, int fee) {
@@ -97,11 +97,11 @@ public:
我在公众号「代码随想录」里将在下一个系列详细讲解动态规划,所以本题解先给出我的C++代码(带详细注释),感兴趣的同学可以自己先学习一下。
-相对于[贪心算法:122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/VsTFA6U96l18Wntjcg3fcg)的动态规划解法中,只需要在计算卖出操作的时候减去手续费就可以了,代码几乎是一样的。
+相对于[贪心算法:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html)的动态规划解法中,只需要在计算卖出操作的时候减去手续费就可以了,代码几乎是一样的。
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices, int fee) {
@@ -126,7 +126,7 @@ public:
C++ 代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices, int fee) {
@@ -216,7 +216,30 @@ class Solution: # 贪心思路
```
Go:
-
+```golang
+func maxProfit(prices []int, fee int) int {
+ var minBuy int = prices[0] //第一天买入
+ var res int
+ for i:=0;i=minBuy&&prices[i]-fee-minBuy<=0{
+ continue
+ }
+ //可以售卖了
+ if prices[i]>minBuy+fee{
+ //累加每天的收益
+ res+=prices[i]-minBuy-fee
+ //更新最小值(如果还在收获利润的区间里,表示并不是真正的卖出,而计算利润每次都要减去手续费,所以要让minBuy = prices[i] - fee;,这样在明天收获利润的时候,才不会多减一次手续费!)
+ minBuy=prices[i]-fee
+ }
+ }
+ return res
+}
+```
Javascript:
```Javascript
// 贪心思路
@@ -246,4 +269,4 @@ var maxProfit = function(prices, fee) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md
index e2b83999..7c54a2fe 100644
--- a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md
+++ b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md
@@ -8,7 +8,7 @@
## 714.买卖股票的最佳时机含手续费
-题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
+[力扣题目链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
@@ -36,7 +36,7 @@
## 思路
-在讲解贪心专题的时候,我们已经讲过本题了[贪心算法:买卖股票的最佳时机含手续费](https://mp.weixin.qq.com/s/olWrUuDEYw2Jx5rMeG7XAg)
+在讲解贪心专题的时候,我们已经讲过本题了[贪心算法:买卖股票的最佳时机含手续费](https://programmercarl.com/0714.买卖股票的最佳时机含手续费.html)
使用贪心算法,的性能是:
* 时间复杂度:O(n)
@@ -44,7 +44,7 @@
那么我们再来看看是使用动规的方法如何解题。
-相对于[动态规划:122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w),本题只需要在计算卖出操作的时候减去手续费就可以了,代码几乎是一样的。
+相对于[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),本题只需要在计算卖出操作的时候减去手续费就可以了,代码几乎是一样的。
唯一差别在于递推公式部分,所以本篇也就不按照动规五部曲详细讲解了,主要讲解一下递推公式部分。
@@ -68,11 +68,11 @@ dp[i][1] 表示第i天不持有股票所得最多现金
所以:dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
-**本题和[动态规划:122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w)的区别就是这里需要多一个减去手续费的操作**。
+**本题和[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html)的区别就是这里需要多一个减去手续费的操作**。
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int maxProfit(vector& prices, int fee) {
@@ -152,6 +152,25 @@ class Solution:
```
Go:
+```Go
+func maxProfit(prices []int, fee int) int {
+ n := len(prices)
+ dp := make([][2]int, n)
+ dp[0][0] = -prices[0]
+ for i := 1; i < n; i++ {
+ dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i]-fee)
+ dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i])
+ }
+ return dp[n-1][1]
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+```
Javascript:
```javascript
@@ -171,4 +190,4 @@ const maxProfit = (prices,fee) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md
index 9ee94a3d..9e3da663 100644
--- a/problems/0718.最长重复子数组.md
+++ b/problems/0718.最长重复子数组.md
@@ -8,7 +8,7 @@
## 718. 最长重复子数组
-题目链接:https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/
+[力扣题目链接](https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/)
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
@@ -74,7 +74,7 @@ dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最
代码如下:
-```C++
+```CPP
for (int i = 1; i <= A.size(); i++) {
for (int j = 1; j <= B.size(); j++) {
if (A[i - 1] == B[j - 1]) {
@@ -94,7 +94,7 @@ for (int i = 1; i <= A.size(); i++) {
以上五部曲分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int findLength(vector& A, vector& B) {
@@ -128,7 +128,7 @@ public:
**此时遍历B数组的时候,就要从后向前遍历,这样避免重复覆盖**。
-```C++
+```CPP
class Solution {
public:
int findLength(vector& A, vector& B) {
@@ -252,10 +252,37 @@ func findLength(A []int, B []int) int {
}
```
+JavaScript:
+
+> 动态规划
+
+```javascript
+const findLength = (A, B) => {
+ // A、B数组的长度
+ const [m, n] = [A.length, B.length];
+ // dp数组初始化,都初始化为0
+ const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0));
+ // 初始化最大长度为0
+ let res = 0;
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ // 遇到A[i - 1] === B[j - 1],则更新dp数组
+ if (A[i - 1] === B[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ }
+ // 更新res
+ res = dp[i][j] > res ? dp[i][j] : res;
+ }
+ }
+ // 遍历完成,返回res
+ return res;
+};
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0724.寻找数组的中心索引.md b/problems/0724.寻找数组的中心索引.md
index bf989979..b4115893 100644
--- a/problems/0724.寻找数组的中心索引.md
+++ b/problems/0724.寻找数组的中心索引.md
@@ -43,7 +43,7 @@
* 判断leftSum和rightSum是否相同
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int pivotIndex(vector& nums) {
@@ -67,11 +67,38 @@ public:
## Java
```java
+class Solution {
+ public int pivotIndex(int[] nums) {
+ int sum = 0;
+ for (int i = 0; i < nums.length; i++) {
+ sum += nums[i]; // 总和
+ }
+ int leftSum = 0;
+ int rightSum = 0;
+ for (int i = 0; i < nums.length; i++) {
+ leftSum += nums[i];
+ rightSum = sum - leftSum + nums[i]; // leftSum 里面已经有 nums[i],多减了一次,所以加上
+ if (leftSum == rightSum) {
+ return i;
+ }
+ }
+ return -1; // 不存在
+ }
+}
```
## 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
@@ -88,6 +115,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0738.单调递增的数字.md b/problems/0738.单调递增的数字.md
index 75c7f250..f63a05b8 100644
--- a/problems/0738.单调递增的数字.md
+++ b/problems/0738.单调递增的数字.md
@@ -8,7 +8,7 @@
## 738.单调递增的数字
-题目链接: https://leetcode-cn.com/problems/monotone-increasing-digits/
+[力扣题目链接](https://leetcode-cn.com/problems/monotone-increasing-digits/)
给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。
@@ -34,7 +34,7 @@
题意很简单,那么首先想的就是暴力解法了,来我替大家暴力一波,结果自然是超时!
代码如下:
-```C++
+```CPP
class Solution {
private:
bool checkNum(int num) {
@@ -87,7 +87,7 @@ public:
C++代码如下:
-```C++
+```CPP
class Solution {
public:
int monotoneIncreasingDigits(int N) {
@@ -159,7 +159,26 @@ class Solution:
```
Go:
-
+```golang
+func monotoneIncreasingDigits(N int) int {
+ s := strconv.Itoa(N)//将数字转为字符串,方便使用下标
+ ss := []byte(s)//将字符串转为byte数组,方便更改。
+ n := len(ss)
+ if n <= 1 {
+ return N
+ }
+ for i:=n-1 ; i>0; i-- {
+ if ss[i-1] > ss[i] {//前一个大于后一位,前一位减1,后面的全部置为9
+ ss[i-1] -= 1
+ for j := i ; j < n; j++ {//后面的全部置为9
+ ss[j] = '9'
+ }
+ }
+ }
+ res, _ := strconv.Atoi(string(ss))
+ return res
+}
+```
Javascript:
```Javascript
var monotoneIncreasingDigits = function(n) {
@@ -190,4 +209,4 @@ var monotoneIncreasingDigits = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md
index 8ad79fe3..b00701ed 100644
--- a/problems/0739.每日温度.md
+++ b/problems/0739.每日温度.md
@@ -9,8 +9,7 @@
# 739. 每日温度
-
-https://leetcode-cn.com/problems/daily-temperatures/
+[力扣题目链接](https://leetcode-cn.com/problems/daily-temperatures/)
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
@@ -117,7 +116,7 @@ T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[
C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -148,7 +147,7 @@ public:
精简代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -283,5 +282,5 @@ func dailyTemperatures(num []int) []int {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md
index 4238a389..eb2a437a 100644
--- a/problems/0746.使用最小花费爬楼梯.md
+++ b/problems/0746.使用最小花费爬楼梯.md
@@ -8,7 +8,7 @@
## 746. 使用最小花费爬楼梯
-题目链接:https://leetcode-cn.com/problems/min-cost-climbing-stairs/
+[力扣题目链接](https://leetcode-cn.com/problems/min-cost-climbing-stairs/)
数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
@@ -34,7 +34,7 @@
## 思路
-这道题目可以说是昨天[动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw)的花费版本。
+这道题目可以说是昨天[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)的花费版本。
**注意题目描述:每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯**
@@ -98,7 +98,7 @@ dp[1] = cost[1];
以上分析完毕,整体C++代码如下:
-```C++
+```CPP
// 版本一
class Solution {
public:
@@ -120,7 +120,7 @@ public:
还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下:
-```C++
+```CPP
// 版本二
class Solution {
public:
@@ -165,7 +165,7 @@ public:
所以代码这么写:
-```C++
+```CPP
class Solution {
public:
int minCostClimbingStairs(vector& cost) {
@@ -185,9 +185,9 @@ public:
# 总结
-大家可以发现这道题目相对于 昨天的[动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw)有难了一点,但整体思路是一样。
+大家可以发现这道题目相对于 昨天的[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)有难了一点,但整体思路是一样。
-从[动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)到 [动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw)再到今天这道题目,录友们感受到循序渐进的梯度了嘛。
+从[动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html)到 [动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)再到今天这道题目,录友们感受到循序渐进的梯度了嘛。
每个系列开始的时候,都有录友和我反馈说题目太简单了,赶紧上难度,但也有录友和我说有点难了,快跟不上了。
@@ -272,4 +272,4 @@ var minCostClimbingStairs = function(cost) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0763.划分字母区间.md b/problems/0763.划分字母区间.md
index b36e00b7..43c663c2 100644
--- a/problems/0763.划分字母区间.md
+++ b/problems/0763.划分字母区间.md
@@ -9,7 +9,7 @@
## 763.划分字母区间
-题目链接: https://leetcode-cn.com/problems/partition-labels/
+[力扣题目链接](https://leetcode-cn.com/problems/partition-labels/)
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
@@ -20,7 +20,7 @@
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
-
+
提示:
* S的长度在[1, 500]之间。
@@ -47,7 +47,7 @@
明白原理之后,代码并不复杂,如下:
-```C++
+```CPP
class Solution {
public:
vector partitionLabels(string S) {
@@ -181,4 +181,4 @@ var partitionLabels = function(s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0841.钥匙和房间.md b/problems/0841.钥匙和房间.md
index 1b3c4a95..4a0185ec 100644
--- a/problems/0841.钥匙和房间.md
+++ b/problems/0841.钥匙和房间.md
@@ -10,7 +10,7 @@
# 841.钥匙和房间
-题目地址:https://leetcode-cn.com/problems/keys-and-rooms/
+[力扣题目链接](https://leetcode-cn.com/problems/keys-and-rooms/)
有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,...,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。
@@ -54,7 +54,7 @@
BFS C++代码代码如下:
-```C++
+```CPP
class Solution {
bool bfs(const vector>& rooms) {
vector visited(rooms.size(), 0); // 标记房间是否被访问过
@@ -89,7 +89,7 @@ public:
DFS C++代码如下:
-```C++
+```CPP
class Solution {
private:
void dfs(int key, const vector>& rooms, vector& visited) {
@@ -131,5 +131,5 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0844.比较含退格的字符串.md b/problems/0844.比较含退格的字符串.md
index 9f37959d..e6bf3493 100644
--- a/problems/0844.比较含退格的字符串.md
+++ b/problems/0844.比较含退格的字符串.md
@@ -9,12 +9,12 @@
# 844.比较含退格的字符串
-题目链接:https://leetcode-cn.com/problems/backspace-string-compare/
+[力扣题目链接](https://leetcode-cn.com/problems/backspace-string-compare/)
给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
-
+
示例 1:
* 输入:S = "ab#c", T = "ad#c"
* 输出:true
@@ -42,7 +42,7 @@
## 普通方法(使用栈的思路)
-这道题目一看就是要使用栈的节奏,这种匹配(消除)问题也是栈的擅长所在,跟着一起刷题的同学应该知道,在[栈与队列:匹配问题都是栈的强项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg),我就已经提过了一次使用栈来做类似的事情了。
+这道题目一看就是要使用栈的节奏,这种匹配(消除)问题也是栈的擅长所在,跟着一起刷题的同学应该知道,在[栈与队列:匹配问题都是栈的强项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html),我就已经提过了一次使用栈来做类似的事情了。
**那么本题,确实可以使用栈的思路,但是没有必要使用栈,因为最后比较的时候还要比较栈里的元素,有点麻烦**。
@@ -50,7 +50,7 @@
代码如下:
-```C++
+```CPP
class Solution {
public:
bool backspaceCompare(string S, string T) {
@@ -78,7 +78,7 @@ public:
当然以上代码,大家可以发现有重复的逻辑处理S,处理T,可以把这块公共逻辑抽离出来,代码精简如下:
-```C++
+```CPP
class Solution {
private:
string getString(const string& S) {
@@ -115,7 +115,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
bool backspaceCompare(string S, string T) {
@@ -160,6 +160,32 @@ public:
Java:
+```java
+// 普通方法(使用栈的思路)
+class Solution {
+ public boolean backspaceCompare(String s, String t) {
+ StringBuilder ssb = new StringBuilder(); // 模拟栈
+ StringBuilder tsb = new StringBuilder(); // 模拟栈
+ // 分别处理两个 String
+ for (char c : s.toCharArray()) {
+ if (c != '#') {
+ ssb.append(c); // 模拟入栈
+ } else if (ssb.length() > 0){ // 栈非空才能弹栈
+ ssb.deleteCharAt(ssb.length() - 1); // 模拟弹栈
+ }
+ }
+ for (char c : t.toCharArray()) {
+ if (c != '#') {
+ tsb.append(c); // 模拟入栈
+ } else if (tsb.length() > 0){ // 栈非空才能弹栈
+ tsb.deleteCharAt(tsb.length() - 1); // 模拟弹栈
+ }
+ }
+ return ssb.toString().equals(tsb.toString());
+ }
+}
+```
+
Python:
Go:
@@ -170,5 +196,5 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0860.柠檬水找零.md b/problems/0860.柠檬水找零.md
index 0d5a7075..46e05419 100644
--- a/problems/0860.柠檬水找零.md
+++ b/problems/0860.柠檬水找零.md
@@ -9,7 +9,7 @@
## 860.柠檬水找零
-题目链接:https://leetcode-cn.com/problems/lemonade-change/
+[力扣题目链接](https://leetcode-cn.com/problems/lemonade-change/)
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。
@@ -82,7 +82,7 @@
C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool lemonadeChange(vector& bills) {
@@ -260,4 +260,4 @@ var lemonadeChange = function(bills) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0922.按奇偶排序数组II.md b/problems/0922.按奇偶排序数组II.md
index bb609c12..97d7091e 100644
--- a/problems/0922.按奇偶排序数组II.md
+++ b/problems/0922.按奇偶排序数组II.md
@@ -32,7 +32,7 @@
其实这道题可以用很朴实的方法,时间复杂度就就是O(n)了,C++代码如下:
-```C++
+```CPP
class Solution {
public:
vector sortArrayByParityII(vector& A) {
@@ -64,7 +64,7 @@ public:
以上代码我是建了两个辅助数组,而且A数组还相当于遍历了两次,用辅助数组的好处就是思路清晰,优化一下就是不用这两个辅助树,代码如下:
-```C++
+```CPP
class Solution {
public:
vector sortArrayByParityII(vector& A) {
@@ -93,7 +93,7 @@ public:
当然还可以在原数组上修改,连result数组都不用了。
-```C++
+```CPP
class Solution {
public:
vector sortArrayByParityII(vector& A) {
@@ -120,11 +120,61 @@ public:
## Java
```java
+// 方法一
+class Solution {
+ public int[] sortArrayByParityII(int[] nums) {
+ // 分别存放 nums 中的奇数、偶数
+ int len = nums.length;
+ int evenIndex = 0;
+ int oddIndex = 0;
+ int[] even = new int[len / 2];
+ int[] odd = new int[len / 2];
+ for (int i = 0; i < len; i++) {
+ if (nums[i] % 2 == 0) {
+ even[evenIndex++] = nums[i];
+ } else {
+ odd[oddIndex++] = nums[i];
+ }
+ }
+ // 把奇偶数组重新存回 nums
+ int index = 0;
+ for (int i = 0; i < even.length; i++) {
+ nums[index++] = even[i];
+ nums[index++] = odd[i];
+ }
+ return nums;
+ }
+}
```
## 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
@@ -141,6 +191,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0925.长按键入.md b/problems/0925.长按键入.md
index 95b6325a..3502f2fb 100644
--- a/problems/0925.长按键入.md
+++ b/problems/0925.长按键入.md
@@ -8,6 +8,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
# 925.长按键入
+[力扣题目链接](https://leetcode-cn.com/problems/long-pressed-name/)
你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。
@@ -59,7 +60,7 @@
上面的逻辑想清楚了,不难写出如下C++代码:
-```C++
+```CPP
class Solution {
public:
bool isLongPressedName(string name, string typed) {
@@ -98,9 +99,62 @@ public:
# 其他语言版本
Java:
-
+```java
+class Solution {
+ public boolean isLongPressedName(String name, String typed) {
+ int i = 0, j = 0;
+ int m = name.length(), n = typed.length();
+ while (i< m && j < n) {
+ if (name.charAt(i) == typed.charAt(j)) { // 相同则同时向后匹配
+ i++; j++;
+ }
+ else {
+ if (j == 0) return false; // 如果是第一位就不相同直接返回false
+ // 判断边界为n-1,若为n会越界,例如name:"kikcxmvzi" typed:"kiikcxxmmvvzzz"
+ while (j < n-1 && typed.charAt(j) == typed.charAt(j-1)) j++;
+ if (name.charAt(i) == typed.charAt(j)) { // j跨越重复项之后再次和name[i]匹配
+ i++; j++; // 相同则同时向后匹配
+ }
+ else return false;
+ }
+ }
+ // 说明name没有匹配完
+ if (i < m) return false;
+ // 说明type没有匹配完
+ while (j < n) {
+ if (typed.charAt(j) == typed.charAt(j-1)) j++;
+ else return false;
+ }
+ return true;
+ }
+}
+```
Python:
-
+```python
+class Solution:
+ def isLongPressedName(self, name: str, typed: str) -> bool:
+ i, j = 0, 0
+ m, n = len(name) , len(typed)
+ while i< m and j < n:
+ if name[i] == typed[j]: # 相同时向后匹配
+ i += 1
+ j += 1
+ else: # 不相同
+ if j == 0: return False # 如果第一位不相同,直接返回false
+ # 判断边界为n-1,若为n会越界,例如name:"kikcxmvzi" typed:"kiikcxxmmvvzzz"
+ while j < n - 1 and typed[j] == typed[j-1]: j += 1
+ if name[i] == typed[j]:
+ i += 1
+ j += 1
+ else: return False
+ # 说明name没有匹配完
+ if i < m: return False
+ # 说明type没有匹配完
+ while j < n:
+ if typed[j] == typed[j-1]: j += 1
+ else: return False
+ return True
+```
Go:
JavaScript:
@@ -109,5 +163,5 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0941.有效的山脉数组.md b/problems/0941.有效的山脉数组.md
index 167cfb1a..ef5739e3 100644
--- a/problems/0941.有效的山脉数组.md
+++ b/problems/0941.有效的山脉数组.md
@@ -1,9 +1,41 @@
-## 题目链接
+
+
+
+
+
+
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-https://leetcode-cn.com/problems/valid-mountain-array/
+# 941.有效的山脉数组
-## 思路
+[力扣题目链接](https://leetcode-cn.com/problems/valid-mountain-array/)
+
+给定一个整数数组 arr,如果它是有效的山脉数组就返回 true,否则返回 false。
+
+让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组:
+
+* arr.length >= 3
+* 在 0 < i < arr.length - 1 条件下,存在 i 使得:
+ * arr[0] < arr[1] < ... arr[i-1] < arr[i]
+ * arr[i] > arr[i+1] > ... > arr[arr.length - 1]
+
+
+
+示例 1:
+* 输入:arr = [2,1]
+* 输出:false
+
+示例 2:
+* 输入:arr = [3,5,5]
+* 输出:false
+
+示例 3:
+* 输入:arr = [0,3,2,1]
+* 输出:true
+
+
+# 思路
判断是山峰,主要就是要严格的保存左边到中间,和右边到中间是递增的。
@@ -38,7 +70,12 @@ public:
}
};
```
-Java 版本如下:
+
+如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://programmercarl.com/双指针总结.html)
+
+# 其他语言版本
+
+## Java
```java
class Solution {
@@ -66,5 +103,26 @@ class Solution {
}
```
-如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
+## Python
+```python
+```
+
+## Go
+
+```go
+```
+
+## JavaScript
+
+```js
+```
+
+-----------------------
+* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
+* B站视频:[代码随想录](https://space.bilibili.com/525438321)
+* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
+
+
+
+
diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md
index a0eb5883..384c9a78 100644
--- a/problems/0968.监控二叉树.md
+++ b/problems/0968.监控二叉树.md
@@ -9,7 +9,7 @@
## 968.监控二叉树
-题目地址 : https://leetcode-cn.com/problems/binary-tree-cameras/
+[力扣题目链接](https://leetcode-cn.com/problems/binary-tree-cameras/)
给定一个二叉树,我们在树的节点上安装摄像头。
@@ -216,7 +216,7 @@ int minCameraCover(TreeNode* root) {
## C++代码
-```C++
+```CPP
// 版本一
class Solution {
private:
@@ -270,7 +270,7 @@ public:
在以上代码的基础上,再进行精简,代码如下:
-```C++
+```CPP
// 版本二
class Solution {
private:
@@ -347,28 +347,88 @@ class Solution {
Python:
```python
+# 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 minCameraCover(self, root: TreeNode) -> int:
+ # Greedy Algo:
+ # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
+ # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
+ # 0: 该节点未覆盖
+ # 1: 该节点有摄像头
+ # 2: 该节点有覆盖
+
result = 0
- def traversal(cur):
+ # 从下往上遍历:后序(左右中)
+ def traversal(curr: TreeNode) -> int:
nonlocal result
- if not cur:
- return 2
- left = traversal(cur.left)
- right = traversal(cur.right)
- if left == 2 and right == 2:
+
+ if not curr: return 2
+ left = traversal(curr.left)
+ right = traversal(curr.right)
+
+ # Case 1:
+ # 左右节点都有覆盖
+ if left == 2 and right == 2:
return 0
- elif left == 0 or right == 0:
+
+ # Case 2:
+ # left == 0 && right == 0 左右节点无覆盖
+ # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
+ # left == 0 && right == 1 左节点有无覆盖,右节点摄像头
+ # left == 0 && right == 2 左节点无覆盖,右节点覆盖
+ # left == 2 && right == 0 左节点覆盖,右节点无覆盖
+ elif left == 0 or right == 0:
result += 1
return 1
+
+ # Case 3:
+ # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
+ # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
+ # left == 1 && right == 1 左右节点都有摄像头
elif left == 1 or right == 1:
return 2
- else: return -1
- if traversal(root) == 0: result += 1
+
+ # 其他情况前段代码均已覆盖
+
+ if traversal(root) == 0:
+ result += 1
+
return result
```
Go:
+```go
+const inf = math.MaxInt64 / 2
+func minCameraCover(root *TreeNode) int {
+ var dfs func(*TreeNode) (a, b, c int)
+ dfs = func(node *TreeNode) (a, b, c int) {
+ if node == nil {
+ return inf, 0, 0
+ }
+ lefta, leftb, leftc := dfs(node.Left)
+ righta, rightb, rightc := dfs(node.Right)
+ a = leftc + rightc + 1
+ b = min(a, min(lefta+rightb, righta+leftb))
+ c = min(a, leftb+rightb)
+ return
+ }
+ _, ans, _ := dfs(root)
+ return ans
+}
+
+func min(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+```
Javascript:
```Javascript
var minCameraCover = function(root) {
@@ -406,9 +466,48 @@ var minCameraCover = function(root) {
};
```
+C:
+```c
+/*
+**函数后序遍历二叉树。判断一个结点状态时,根据其左右孩子结点的状态进行判断
+**状态:0为没有被摄像头覆盖到。1为此结点处应设置摄像头。2为此结点已被摄像头覆盖
+*/
+int traversal(struct TreeNode* node, int* ans) {
+ //递归结束条件:传入结点为NULL,假设此结点能被摄像头覆盖。这样方便与对叶子结点的判断,将叶子结点设为0
+ if(!node)
+ return 2;
+ //后序遍历二叉树,记录左右孩子的状态。根据左右孩子状态更新结点自身状态
+ int left = traversal(node->left, ans);
+ int right = traversal(node->right, ans);
+
+ //若左右孩子都可以被摄像头覆盖,将父亲结点状态设为0
+ if(left == 2 && right == 2) {
+ return 0;
+ }
+ //若左右孩子有一个结点状态为没有被覆盖(0),则将父亲结点状态设置为摄像头
+ if(left == 0 || right == 0) {
+ (*ans)++;
+ return 1;
+ }
+ //若左右孩子有一个为摄像头,证明父亲结点可以被覆盖。将父亲结点状态变为2
+ if(left == 1 || right == 1)
+ return 2;
+ //逻辑不会走到-1,语句不会执行
+ return -1;
+}
+
+int minCameraCover(struct TreeNode* root){
+ int ans = 0;
+
+ //在对整个二叉树遍历后。头结点可能未被覆盖,这时候如果函数返回值为0,证明头结点未被覆盖。说明头结点也需要添置摄像头,ans++
+ if(traversal(root, &ans) == 0)
+ ans++;
+ return ans;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md
index 0f9007d7..a57af3c1 100644
--- a/problems/0977.有序数组的平方.md
+++ b/problems/0977.有序数组的平方.md
@@ -10,7 +10,7 @@
# 977.有序数组的平方
-https://leetcode-cn.com/problems/squares-of-a-sorted-array/
+[力扣题目链接](https://leetcode-cn.com/problems/squares-of-a-sorted-array/)
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
@@ -29,7 +29,7 @@ https://leetcode-cn.com/problems/squares-of-a-sorted-array/
最直观的相反,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
-```C++
+```CPP
class Solution {
public:
vector sortedSquares(vector& A) {
@@ -64,7 +64,7 @@ public:
不难写出如下代码:
-```C++
+```CPP
class Solution {
public:
vector sortedSquares(vector& A) {
@@ -224,8 +224,119 @@ const sortedSquares = function (nums) {
}
```
+Swift:
+
+```swift
+func sortedSquares(_ nums: [Int]) -> [Int] {
+ // 指向新数组最后一个元素
+ var k = nums.count - 1
+ // 指向原数组第一个元素
+ var i = 0
+ // 指向原数组最后一个元素
+ var j = nums.count - 1
+ // 初始化新数组(用-1填充)
+ var result = Array(repeating: -1, count: nums.count)
+
+ for _ in 0.. nums[right]**2
+ result << nums[left]**2
+ left += 1
+ else
+ result << nums[right]**2
+ right -= 1
+ end
+ end
+ result.reverse
+end
+```
+
+
+C:
+```c
+int* sortedSquares(int* nums, int numsSize, int* returnSize){
+ //返回的数组大小就是原数组大小
+ *returnSize = numsSize;
+ //创建两个指针,right指向数组最后一位元素,left指向数组第一位元素
+ int right = numsSize - 1;
+ int left = 0;
+
+ //最后要返回的结果数组
+ int* ans = (int*)malloc(sizeof(int) * numsSize);
+ int index;
+ for(index = numsSize - 1; index >= 0; index--) {
+ //左指针指向元素的平方
+ int lSquare = nums[left] * nums[left];
+ //右指针指向元素的平方
+ int rSquare = nums[right] * nums[right];
+ //若左指针指向元素平方比右指针指向元素平方大,将左指针指向元素平方放入结果数组。左指针右移一位
+ if(lSquare > rSquare) {
+ ans[index] = lSquare;
+ left++;
+ }
+ //若右指针指向元素平方比左指针指向元素平方大,将右指针指向元素平方放入结果数组。右指针左移一位
+ else {
+ ans[index] = rSquare;
+ right--;
+ }
+ }
+ //返回结果数组
+ return ans;
+}
+```
+
+PHP:
+```php
+class Solution {
+ /**
+ * @param Integer[] $nums
+ * @return Integer[]
+ */
+ function sortedSquares($nums) {
+ // 双指针法
+ $res = [];
+ for ($i = 0; $i < count($nums); $i++) {
+ $res[$i] = 0;
+ }
+ $k = count($nums) - 1;
+ for ($i = 0, $j = count($nums) - 1; $i <= $j; ) {
+ if ($nums[$i] ** 2 < $nums[$j] ** 2) {
+ $res[$k--] = $nums[$j] ** 2;
+ $j--;
+ }
+ else {
+ $res[$k--] = $nums[$i] ** 2;
+ $i++;
+ }
+ }
+ return $res;
+ }
+}
+```
+
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md
index 875340c4..e02780da 100644
--- a/problems/1002.查找常用字符.md
+++ b/problems/1002.查找常用字符.md
@@ -10,7 +10,7 @@
# 1002. 查找常用字符
-https://leetcode-cn.com/problems/find-common-characters/
+[力扣题目链接](https://leetcode-cn.com/problems/find-common-characters/)
给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。
@@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/find-common-characters/
【示例二】
输入:["cool","lock","cook"]
输出:["c","o"]
-
+
# 思路
@@ -40,9 +40,9 @@ https://leetcode-cn.com/problems/find-common-characters/
可以看出这是指数级别的时间复杂度,非常高,而且代码实现也不容易,因为要统计 重复的字符,还要适当的替换或者去重。
-那我们还是哈希法吧。如果对哈希法不了解,可以看这篇:[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。
+那我们还是哈希法吧。如果对哈希法不了解,可以看这篇:[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)。
-如果对用数组来做哈希法不了解的话,可以看这篇:[把数组当做哈希表来用,很巧妙!](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)。
+如果对用数组来做哈希法不了解的话,可以看这篇:[把数组当做哈希表来用,很巧妙!](https://programmercarl.com/0242.有效的字母异位词.html)。
了解了哈希法,理解了数组在哈希法中的应用之后,可以来看解题思路了。
@@ -97,7 +97,7 @@ for (int i = 0; i < 26; i++) {
整体C++代码如下:
-```C++
+```CPP
class Solution {
public:
vector commonChars(vector& A) {
@@ -169,9 +169,168 @@ class Solution {
}
}
```
+Python
+```python
+class Solution:
+ def commonChars(self, words: List[str]) -> List[str]:
+ if not words: return []
+ result = []
+ hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率
+ for i, c in enumerate(words[0]): # 用第一个字符串给hash初始化
+ hash[ord(c) - ord('a')] += 1
+ # 统计除第一个字符串外字符的出现频率
+ for i in range(1, len(words)):
+ hashOtherStr = [0] * 26
+ for j in range(len(words[i])):
+ hashOtherStr[ord(words[i][j]) - ord('a')] += 1
+ # 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数
+ for k in range(26):
+ hash[k] = min(hash[k], hashOtherStr[k])
+ # 将hash统计的字符次数,转成输出形式
+ for i in range(26):
+ while hash[i] != 0: # 注意这里是while,多个重复的字符
+ result.extend(chr(i + ord('a')))
+ hash[i] -= 1
+ return result
+```
+
+Python 3 使用collections.Counter
+```python
+class Solution:
+ def commonChars(self, words: List[str]) -> List[str]:
+ tmp = collections.Counter(words[0])
+ l = []
+ for i in range(1,len(words)):
+ # 使用 & 取交集
+ tmp = tmp & collections.Counter(words[i])
+
+ # 剩下的就是每个单词都出现的字符(键),个数(值)
+ for j in tmp:
+ v = tmp[j]
+ while(v):
+ l.append(j)
+ v -= 1
+ return l
+```
+
+javaScript
+```js
+var commonChars = function (words) {
+ let res = []
+ let size = 26
+ let firstHash = new Array(size)
+ for (let i = 0; i < size; i++) { // 初始化 hash 数组
+ firstHash[i] = 0
+ }
+
+ let a = "a".charCodeAt()
+ let firstWord = words[0]
+ for (let i = 0; i < firstWord.length; i++) { // 第 0 个单词的统计
+ let idx = firstWord[i].charCodeAt()
+ firstHash[idx - a] += 1
+ }
+
+ for (let i = 1; i < words.length; i++) { // 1-n 个单词统计
+ let otherHash = new Array(size)
+ for (let i = 0; i < size; i++) { // 初始化 hash 数组
+ otherHash[i] = 0
+ }
+
+ for (let j = 0; j < words[i].length; j++) {
+ let idx = words[i][j].charCodeAt()
+ otherHash[idx - a] += 1
+ }
+ for (let i = 0; i < size; i++) {
+ firstHash[i] = Math.min(firstHash[i], otherHash[i])
+ }
+ }
+ for (let i = 0; i < size; i++) {
+ while (firstHash[i] > 0) {
+ res.push(String.fromCharCode(i + a))
+ firstHash[i]--
+ }
+ }
+ return res
+};
+```
+GO
+```golang
+func commonChars(words []string) []string {
+ length:=len(words)
+ fre:=make([][]int,0)//统计每个字符串的词频
+ res:=make([]string,0)
+ //统计词频
+ for i:=0;ib{
+ return b
+ }
+ return a
+}
+```
+
+Swift:
+```swift
+func commonChars(_ words: [String]) -> [String] {
+ var res = [String]()
+ if words.count < 1 {
+ return res
+ }
+ let aUnicodeScalarValue = "a".unicodeScalars.first!.value
+ let lettersMaxCount = 26
+ // 用于统计所有字符串每个字母出现的 最小 频率
+ var hash = Array(repeating: 0, count: lettersMaxCount)
+ // 统计第一个字符串每个字母出现的次数
+ for unicodeScalar in words.first!.unicodeScalars {
+ hash[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1
+ }
+ // 统计除第一个字符串每个字母出现的次数
+ for idx in 1 ..< words.count {
+ var hashOtherStr = Array(repeating: 0, count: lettersMaxCount)
+ for unicodeScalar in words[idx].unicodeScalars {
+ hashOtherStr[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1
+ }
+ // 更新hash,保证hash里统计的字母为出现的最小频率
+ for k in 0 ..< lettersMaxCount {
+ hash[k] = min(hash[k], hashOtherStr[k])
+ }
+ }
+ // 将hash统计的字符次数,转成输出形式
+ for i in 0 ..< lettersMaxCount {
+ while hash[i] != 0 { // 注意这里是while,多个重复的字符
+ let currentUnicodeScalarValue: UInt32 = UInt32(i) + aUnicodeScalarValue
+ let currentUnicodeScalar: UnicodeScalar = UnicodeScalar(currentUnicodeScalarValue)!
+ let outputStr = String(currentUnicodeScalar) // UnicodeScalar -> String
+ res.append(outputStr)
+ hash[i] -= 1
+ }
+ }
+ return res
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md
index 3534e1e2..020476a9 100644
--- a/problems/1005.K次取反后最大化的数组和.md
+++ b/problems/1005.K次取反后最大化的数组和.md
@@ -9,7 +9,7 @@
## 1005.K次取反后最大化的数组和
-题目地址:https://leetcode-cn.com/problems/maximize-sum-of-array-after-k-negations/
+[力扣题目链接](https://leetcode-cn.com/problems/maximize-sum-of-array-after-k-negations/)
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
@@ -61,7 +61,7 @@
对应C++代码如下:
-```C++
+```CPP
class Solution {
static bool cmp(int a, int b) {
return abs(a) > abs(b);
@@ -110,18 +110,16 @@ class Solution {
int len = nums.length;
for (int i = 0; i < len; i++) {
//从前向后遍历,遇到负数将其变为正数,同时K--
- if (nums[i] < 0 && k > 0) {
+ if (nums[i] < 0 && K > 0) {
nums[i] = -nums[i];
- k--;
+ K--;
}
}
// 如果K还大于0,那么反复转变数值最小的元素,将K用完
- if (k % 2 == 1) nums[len - 1] = -nums[len - 1];
- int result = 0;
- for (int a : nums) {
- result += a;
- }
- return result;
+
+ if (K % 2 == 1) nums[len - 1] = -nums[len - 1];
+ return Arrays.stream(nums).sum();
+
}
}
```
@@ -219,4 +217,4 @@ var largestSumAfterKNegations = function(nums, k) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md
index cb74ef75..b71a5199 100644
--- a/problems/1035.不相交的线.md
+++ b/problems/1035.不相交的线.md
@@ -8,7 +8,7 @@
## 1035.不相交的线
-题目链接: https://leetcode-cn.com/problems/uncrossed-lines/
+[力扣题目链接](https://leetcode-cn.com/problems/uncrossed-lines/)
我们在两条独立的水平线上按给定的顺序写下 A 和 B 中的整数。
@@ -35,17 +35,17 @@
这么分析完之后,大家可以发现:**本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!**
-那么本题就和我们刚刚讲过的这道题目[动态规划:1143.最长公共子序列](https://mp.weixin.qq.com/s/Qq0q4HaE4TyasCTj2WGFOg)就是一样一样的了。
+那么本题就和我们刚刚讲过的这道题目[动态规划:1143.最长公共子序列](https://programmercarl.com/1143.最长公共子序列.html)就是一样一样的了。
一样到什么程度呢? 把字符串名字改一下,其他代码都不用改,直接copy过来就行了。
-其实本题就是求最长公共子序列的长度,介于我们刚刚讲过[动态规划:1143.最长公共子序列](https://mp.weixin.qq.com/s/Qq0q4HaE4TyasCTj2WGFOg),所以本题我就不再做动规五部曲分析了。
+其实本题就是求最长公共子序列的长度,介于我们刚刚讲过[动态规划:1143.最长公共子序列](https://programmercarl.com/1143.最长公共子序列.html),所以本题我就不再做动规五部曲分析了。
-如果大家有点遗忘了最长公共子序列,就再看一下这篇:[动态规划:1143.最长公共子序列](https://mp.weixin.qq.com/s/Qq0q4HaE4TyasCTj2WGFOg)
+如果大家有点遗忘了最长公共子序列,就再看一下这篇:[动态规划:1143.最长公共子序列](https://programmercarl.com/1143.最长公共子序列.html)
本题代码如下:
-```C++
+```CPP
class Solution {
public:
int maxUncrossedLines(vector& A, vector& B) {
@@ -66,9 +66,9 @@ public:
## 总结
-看到代码大家也可以发现其实就是求两个字符串的最长公共子序列,但如果没有做过[1143.最长公共子序列](https://mp.weixin.qq.com/s/Qq0q4HaE4TyasCTj2WGFOg),本题其实还有很有难度的。
+看到代码大家也可以发现其实就是求两个字符串的最长公共子序列,但如果没有做过[1143.最长公共子序列](https://programmercarl.com/1143.最长公共子序列.html),本题其实还有很有难度的。
-这是Carl为什么要先讲[1143.最长公共子序列](https://mp.weixin.qq.com/s/Qq0q4HaE4TyasCTj2WGFOg)再讲本题,大家会发现一个正确的刷题顺序对算法学习是非常重要的!
+这是Carl为什么要先讲[1143.最长公共子序列](https://programmercarl.com/1143.最长公共子序列.html)再讲本题,大家会发现一个正确的刷题顺序对算法学习是非常重要的!
这也是Carl做了很多题目(包括ACM和力扣)才总结出来的规律,大家仔细体会一下哈。
@@ -109,7 +109,28 @@ class Solution:
return dp[-1][-1]
```
-Go:
+JavaScript:
+
+```javascript
+const maxUncrossedLines = (nums1, nums2) => {
+ // 两个数组长度
+ const [m, n] = [nums1.length, nums2.length];
+ // 创建dp数组并都初始化为0
+ const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0));
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ // 根据两种情况更新dp[i][j]
+ if (nums1[i - 1] === nums2[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+ // 返回dp数组中右下角的元素
+ return dp[m][n];
+};
+```
@@ -118,4 +139,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md
index c6a49376..b88fd618 100644
--- a/problems/1047.删除字符串中的所有相邻重复项.md
+++ b/problems/1047.删除字符串中的所有相邻重复项.md
@@ -13,7 +13,7 @@
# 1047. 删除字符串中的所有相邻重复项
-https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
+[力扣题目链接](https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/)
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
@@ -26,7 +26,7 @@ https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
* 输入:"abbaca"
* 输出:"ca"
* 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
-
+
提示:
* 1 <= S.length <= 20000
@@ -69,7 +69,7 @@ https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
C++代码 :
-```C++
+```CPP
class Solution {
public:
string removeDuplicates(string S) {
@@ -97,7 +97,7 @@ public:
代码如下:
-```C++
+```CPP
class Solution {
public:
string removeDuplicates(string S) {
@@ -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:
@@ -252,4 +275,4 @@ var removeDuplicates = function(s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md
index 59c59189..795d923c 100644
--- a/problems/1049.最后一块石头的重量II.md
+++ b/problems/1049.最后一块石头的重量II.md
@@ -9,7 +9,7 @@
## 1049. 最后一块石头的重量 II
-题目链接:https://leetcode-cn.com/problems/last-stone-weight-ii/
+[力扣题目链接](https://leetcode-cn.com/problems/last-stone-weight-ii/)
题目难度:中等
@@ -29,7 +29,7 @@
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
-
+
提示:
* 1 <= stones.length <= 30
@@ -39,12 +39,12 @@
如果对背包问题不都熟悉先看这两篇:
-* [动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)
-* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)
+* [动态规划:关于01背包问题,你该了解这些!](https://programmercarl.com/背包理论基础01背包-1.html)
+* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)
本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,**这样就化解成01背包问题了**。
-是不是感觉和昨天讲解的[416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)非常像了。
+是不是感觉和昨天讲解的[416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)非常像了。
本题物品的重量为store[i],物品的价值也为store[i]。
@@ -89,11 +89,11 @@ vector dp(15001, 0);
4. 确定遍历顺序
-在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒叙遍历!
+在[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒叙遍历!
代码如下:
-```C++
+```CPP
for (int i = 0; i < stones.size(); i++) { // 遍历物品
for (int j = target; j >= stones[i]; j--) { // 遍历背包
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
@@ -119,7 +119,7 @@ for (int i = 0; i < stones.size(); i++) { // 遍历物品
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int lastStoneWeightII(vector& stones) {
@@ -143,9 +143,9 @@ public:
## 总结
-本题其实和[416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)几乎是一样的,只是最后对dp[target]的处理方式不同。
+本题其实和[416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)几乎是一样的,只是最后对dp[target]的处理方式不同。
-[416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)相当于是求背包是否正好装满,而本题是求背包最多能装多少。
+[416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)相当于是求背包是否正好装满,而本题是求背包最多能装多少。
@@ -246,4 +246,4 @@ var lastStoneWeightII = function (stones) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md
index e1fc1abb..b3b5e6c0 100644
--- a/problems/1143.最长公共子序列.md
+++ b/problems/1143.最长公共子序列.md
@@ -8,7 +8,7 @@
## 1143.最长公共子序列
-题目链接: https://leetcode-cn.com/problems/longest-common-subsequence/
+[力扣题目链接](https://leetcode-cn.com/problems/longest-common-subsequence/)
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
@@ -41,7 +41,7 @@
## 思路
-本题和[动态规划:718. 最长重复子数组](https://mp.weixin.qq.com/s/U5WaWqBwdoxzQDotOdWqZg)区别在于这里不要求是连续的了,但要有相对顺序,即:"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
+本题和[动态规划:718. 最长重复子数组](https://programmercarl.com/0718.最长重复子数组.html)区别在于这里不要求是连续的了,但要有相对顺序,即:"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
继续动规五部曲分析如下:
@@ -65,7 +65,7 @@ dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符
代码如下:
-```C++
+```CPP
if (text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
@@ -107,7 +107,7 @@ vector> dp(text1.size() + 1, vector(text2.size() + 1, 0));
以上分析完毕,C++代码如下:
-```C++
+```CPP
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
@@ -221,4 +221,4 @@ const longestCommonSubsequence = (text1, text2) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1207.独一无二的出现次数.md b/problems/1207.独一无二的出现次数.md
index c1720430..027c9f5a 100644
--- a/problems/1207.独一无二的出现次数.md
+++ b/problems/1207.独一无二的出现次数.md
@@ -8,7 +8,7 @@
# 1207.独一无二的出现次数
-链接:https://leetcode-cn.com/problems/unique-number-of-occurrences/
+[力扣题目链接](https://leetcode-cn.com/problems/unique-number-of-occurrences/)
给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。
@@ -35,9 +35,9 @@
# 思路
-这道题目数组在是哈希法中的经典应用,如果对数组在哈希法中的使用还不熟悉的同学可以看这两篇:[数组在哈希法中的应用](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)和[哈希法:383. 赎金信](https://mp.weixin.qq.com/s/qAXqv--UERmiJNNpuphOUQ)
+这道题目数组在是哈希法中的经典应用,如果对数组在哈希法中的使用还不熟悉的同学可以看这两篇:[数组在哈希法中的应用](https://programmercarl.com/0242.有效的字母异位词.html)和[哈希法:383. 赎金信](https://programmercarl.com/0383.赎金信.html)
-进而可以学习一下[set在哈希法中的应用](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q),以及[map在哈希法中的应用](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)
+进而可以学习一下[set在哈希法中的应用](https://programmercarl.com/0349.两个数组的交集.html),以及[map在哈希法中的应用](https://programmercarl.com/0001.两数之和.html)
回归本题,**本题强调了-1000 <= arr[i] <= 1000**,那么就可以用数组来做哈希,arr[i]作为哈希表(数组)的下标,那么arr[i]可以是负数,怎么办?负数不能做数组下标。
@@ -53,7 +53,7 @@
C++代码如下:
-```C++
+```CPP
class Solution {
public:
bool uniqueOccurrences(vector& arr) {
@@ -100,7 +100,21 @@ class Solution {
```
Python:
-
+```python
+class Solution:
+ def uniqueOccurrences(self, arr: List[int]) -> bool:
+ count = [0] * 2002
+ for i in range(len(arr)):
+ count[arr[i] + 1000] += 1 # 防止负数作为下标
+ freq = [False] * 1002 # 标记相同频率是否重复出现
+ for i in range(2001):
+ if count[i] > 0:
+ if freq[count[i]] == False:
+ freq[count[i]] = True
+ else:
+ return False
+ return True
+```
Go:
JavaScript:
@@ -109,5 +123,5 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1221.分割平衡字符串.md b/problems/1221.分割平衡字符串.md
new file mode 100644
index 00000000..c764e3ff
--- /dev/null
+++ b/problems/1221.分割平衡字符串.md
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
+# 1221. 分割平衡字符串
+
+[力扣题目链接](https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/)
+
+在一个 平衡字符串 中,'L' 和 'R' 字符的数量是相同的。
+
+给你一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
+
+注意:分割得到的每个字符串都必须是平衡字符串。
+
+返回可以通过分割得到的平衡字符串的 最大数量 。
+
+
+示例 1:
+
+* 输入:s = "RLRRLLRLRL"
+* 输出:4
+* 解释:s 可以分割为 "RL"、"RRLL"、"RL"、"RL" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
+
+示例 2:
+* 输入:s = "RLLLLRRRLR"
+* 输出:3
+* 解释:s 可以分割为 "RL"、"LLLRRR"、"LR" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
+
+示例 3:
+* 输入:s = "LLLLRRRR"
+* 输出:1
+* 解释:s 只能保持原样 "LLLLRRRR".
+
+示例 4:
+* 输入:s = "RLRRRLLRLL"
+* 输出:2
+* 解释:s 可以分割为 "RL"、"RRRLLRLL" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
+
+# 思路
+
+这道题目看起来好像很复杂,其实是非常简单的贪心,关于贪心,我在这里[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html)有详细的讲解。
+
+从前向后遍历,只要遇到平衡子串,计数就+1,遍历一遍即可。
+
+局部最优:从前向后遍历,只要遇到平衡子串 就统计
+
+全局最优:统计了最多的平衡子串。
+
+局部最优可以推出全局最优,举不出反例,那么就试试贪心。
+
+
+例如,LRLR 这本身就是平衡子串 , 但要遇到LR就可以分割。
+
+C++代码如下:
+
+```CPP
+class Solution {
+public:
+ int balancedStringSplit(string s) {
+ int result = 0;
+ int count = 0;
+ for (int i = 0; i < s.size(); i++) {
+ if (s[i] == 'R') count++;
+ else count--;
+ if (count == 0) result++;
+ }
+ return result;
+ }
+};
+```
+
+# 拓展
+
+一些同学可能想,你这个推理不靠谱,都没有数学证明。怎么就能说是合理的呢,怎么就能说明 局部最优可以推出全局最优呢?
+
+一般数学证明有如下两种方法:
+
+* 数学归纳法
+* 反证法
+
+如果真的去严格数学证明其实不是在我们刷题或者 面试的考察范围内了。
+
+所以贪心题目的思考过程是: 如果发现局部最优好像可以推出全局最优,那么就 尝试一下举反例,如果举不出反例,那么就试试贪心。
+
+
+
+# 其他语言版本
+
+## Java
+
+```java
+```
+
+## Python
+
+```python
+```
+
+## Go
+
+```go
+```
+
+## JavaScript
+
+```js
+```
+
+-----------------------
+* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
+* B站视频:[代码随想录](https://space.bilibili.com/525438321)
+* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
+
+
diff --git a/problems/1356.根据数字二进制下1的数目排序.md b/problems/1356.根据数字二进制下1的数目排序.md
index e464f716..06c29500 100644
--- a/problems/1356.根据数字二进制下1的数目排序.md
+++ b/problems/1356.根据数字二进制下1的数目排序.md
@@ -7,112 +7,115 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-# 1365.有多少小于当前数字的数字
+
+# 1356. 根据数字二进制下 1 的数目排序
+
+[力扣题目链接](https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits/)
题目链接:https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits/
-给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。
+给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。
-换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。
+如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。
-以数组形式返回答案。
-
+请你返回排序后的数组。
示例 1:
-输入:nums = [8,1,2,2,3]
-输出:[4,0,1,1,3]
-解释:
-对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。
-对于 nums[1]=1 不存在比它小的数字。
-对于 nums[2]=2 存在一个比它小的数字:(1)。
-对于 nums[3]=2 存在一个比它小的数字:(1)。
-对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。
+* 输入:arr = [0,1,2,3,4,5,6,7,8]
+* 输出:[0,1,2,4,8,3,5,6,7]
+* 解释:[0] 是唯一一个有 0 个 1 的数。
+[1,2,4,8] 都有 1 个 1 。
+[3,5,6] 有 2 个 1 。
+[7] 有 3 个 1 。按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7]
+
示例 2:
-输入:nums = [6,5,4,8]
-输出:[2,1,0,3]
+* 输入:arr = [1024,512,256,128,64,32,16,8,4,2,1]
+* 输出:[1,2,4,8,16,32,64,128,256,512,1024]
+* 解释:数组中所有整数二进制下都只有 1 个 1 ,所以你需要按照数值大小将它们排序。
示例 3:
-输入:nums = [7,7,7,7]
-输出:[0,0,0,0]
-
-提示:
-* 2 <= nums.length <= 500
-* 0 <= nums[i] <= 100
+* 输入:arr = [10000,10000]
+* 输出:[10000,10000]
+
+示例 4:
+* 输入:arr = [2,3,5,7,11,13,17,19]
+* 输出:[2,3,5,17,7,11,13,19]
+
+示例 5:
+* 输入:arr = [10,100,1000,10000]
+* 输出:[10,100,10000,1000]
+
+
# 思路
-两层for循环暴力查找,时间复杂度明显为O(n^2)。
+这道题其实是考察如何计算一个数的二进制中1的数量。
-那么我们来看一下如何优化。
+我提供两种方法:
-首先要找小于当前数字的数字,那么从小到大排序之后,该数字之前的数字就都是比它小的了。
+* 方法一:
-所以可以定义一个新数组,将数组排个序。
-
-**排序之后,其实每一个数值的下标就代表这前面有几个比它小的了**。
-
-代码如下:
-
-```
-vector vec = nums;
-sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是小于当前数字的数字
-```
-
-此时用一个哈希表hash(本题可以就用一个数组)来做数值和下标的映射。这样就可以通过数值快速知道下标(也就是前面有几个比它小的)。
-
-此时有一个情况,就是数值相同怎么办?
-
-例如,数组:1 2 3 4 4 4 ,第一个数值4的下标是3,第二个数值4的下标是4了。
-
-这里就需要一个技巧了,**在构造数组hash的时候,从后向前遍历,这样hash里存放的就是相同元素最左面的数值和下标了**。
-代码如下:
+朴实无华挨个计算1的数量,最多就是循环n的二进制位数,32位。
```C++
-int hash[101];
-for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标
- hash[vec[i]] = i;
+int bitCount(int n) {
+ int count = 0; // 计数器
+ while (n > 0) {
+ if((n & 1) == 1) count++; // 当前位是1,count++
+ n >>= 1 ; // n向右移位
+ }
+ return count;
}
```
-最后在遍历原数组nums,用hash快速找到每一个数值 对应的 小于这个数值的个数。存放在将结果存放在另一个数组中。
+* 方法二
-代码如下:
+这种方法,只循环n的二进制中1的个数次,比方法一高效的多
```C++
-// 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数
-for (int i = 0; i < nums.size(); i++) {
- vec[i] = hash[nums[i]];
+int bitCount(int n) {
+ int count = 0;
+ while (n) {
+ n &= (n - 1); // 清除最低位的1
+ count++;
+ }
+ return count;
}
```
+以计算12的二进制1的数量为例,如图所示:
-流程如图:
+
-
+下面我就使用方法二,来做这道题目:
-关键地方讲完了,整体C++代码如下:
+## C++代码
```C++
class Solution {
+private:
+ static int bitCount(int n) { // 计算n的二进制中1的数量
+ int count = 0;
+ while(n) {
+ n &= (n -1); // 清除最低位的1
+ count++;
+ }
+ return count;
+ }
+ static bool cmp(int a, int b) {
+ int bitA = bitCount(a);
+ int bitB = bitCount(b);
+ if (bitA == bitB) return a < b; // 如果bit中1数量相同,比较数值大小
+ return bitA < bitB; // 否则比较bit中1数量大小
+ }
public:
- vector smallerNumbersThanCurrent(vector& nums) {
- vector vec = nums;
- sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是小于当前数字的数字
- int hash[101];
- for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标
- hash[vec[i]] = i;
- }
- // 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数
- for (int i = 0; i < nums.size(); i++) {
- vec[i] = hash[nums[i]];
- }
- return vec;
+ vector sortByBits(vector& arr) {
+ sort(arr.begin(), arr.end(), cmp);
+ return arr;
}
};
```
-可以排序之后加哈希,时间复杂度为O(nlogn)
-
# 其他语言版本
@@ -120,8 +123,36 @@ public:
## Java
```java
+class Solution {
+ private int cntInt(int val){
+ int count = 0;
+ while(val > 0) {
+ val = val & (val - 1);
+ count ++;
+ }
+
+ return count;
+ }
+
+ public int[] sortByBits(int[] arr) {
+ return Arrays.stream(arr).boxed()
+ .sorted(new Comparator(){
+ @Override
+ public int compare(Integer o1, Integer o2) {
+ int cnt1 = cntInt(o1);
+ int cnt2 = cntInt(o2);
+ return (cnt1 == cnt2) ? Integer.compare(o1, o2) : Integer.compare(cnt1, cnt2);
+ }
+ })
+ .mapToInt(Integer::intValue)
+ .toArray();
+ }
+}
```
+
+
+
## Python
```python
@@ -141,6 +172,5 @@ public:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/1365.有多少小于当前数字的数字.md b/problems/1365.有多少小于当前数字的数字.md
index 6324329c..5cf6b2d8 100644
--- a/problems/1365.有多少小于当前数字的数字.md
+++ b/problems/1365.有多少小于当前数字的数字.md
@@ -10,14 +10,14 @@
# 1365.有多少小于当前数字的数字
-题目链接:https://leetcode-cn.com/problems/how-many-numbers-are-smaller-than-the-current-number/
+[力扣题目链接](https://leetcode-cn.com/problems/how-many-numbers-are-smaller-than-the-current-number/)
给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。
换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。
以数组形式返回答案。
-
+
示例 1:
* 输入:nums = [8,1,2,2,3]
@@ -36,7 +36,7 @@
示例 3:
* 输入:nums = [7,7,7,7]
* 输出:[0,0,0,0]
-
+
提示:
* 2 <= nums.length <= 500
* 0 <= nums[i] <= 100
@@ -69,7 +69,7 @@ sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是
这里就需要一个技巧了,**在构造数组hash的时候,从后向前遍历,这样hash里存放的就是相同元素最左面的数值和下标了**。
代码如下:
-```C++
+```CPP
int hash[101];
for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标
hash[vec[i]] = i;
@@ -80,7 +80,7 @@ for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对
代码如下:
-```C++
+```CPP
// 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数
for (int i = 0; i < nums.size(); i++) {
vec[i] = hash[nums[i]];
@@ -93,7 +93,7 @@ for (int i = 0; i < nums.size(); i++) {
关键地方讲完了,整体C++代码如下:
-```C++
+```CPP
class Solution {
public:
vector smallerNumbersThanCurrent(vector& nums) {
@@ -139,7 +139,19 @@ public int[] smallerNumbersThanCurrent(int[] nums) {
```
Python:
-
+```python
+class Solution:
+ def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
+ res = nums[:]
+ hash = dict()
+ res.sort() # 从小到大排序之后,元素下标就是小于当前数字的数字
+ for i, num in enumerate(res):
+ if num not in hash.keys(): # 遇到了相同的数字,那么不需要更新该 number 的情况
+ hash[num] = i
+ for i, num in enumerate(nums):
+ res[i] = hash[num]
+ return res
+```
Go:
JavaScript:
@@ -150,4 +162,4 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1382.将二叉搜索树变平衡.md b/problems/1382.将二叉搜索树变平衡.md
index 268f21c5..bce58c33 100644
--- a/problems/1382.将二叉搜索树变平衡.md
+++ b/problems/1382.将二叉搜索树变平衡.md
@@ -9,7 +9,7 @@
# 1382.将二叉搜索树变平衡
-题目地址:https://leetcode-cn.com/problems/balance-a-binary-search-tree/
+[力扣题目链接](https://leetcode-cn.com/problems/balance-a-binary-search-tree/)
给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。
@@ -35,14 +35,14 @@
这道题目,可以中序遍历把二叉树转变为有序数组,然后在根据有序数组构造平衡二叉搜索树。
建议做这道题之前,先看如下两篇题解:
-* [98.验证二叉搜索树](https://mp.weixin.qq.com/s/8odY9iUX5eSi0eRFSXFD4Q) 学习二叉搜索树的特性
-* [108.将有序数组转换为二叉搜索树](https://mp.weixin.qq.com/s/sy3ygnouaZVJs8lhFgl9mw) 学习如何通过有序数组构造二叉搜索树
+* [98.验证二叉搜索树](https://programmercarl.com/0098.验证二叉搜索树.html) 学习二叉搜索树的特性
+* [108.将有序数组转换为二叉搜索树](https://programmercarl.com/0108.将有序数组转换为二叉搜索树.html) 学习如何通过有序数组构造二叉搜索树
这两道题目做过之后,本题分分钟就可以做出来了。
代码如下:
-```C++
+```CPP
class Solution {
private:
vector vec;
@@ -55,7 +55,7 @@ private:
vec.push_back(cur->val);
traversal(cur->right);
}
- 有序数组转平衡二叉树
+ // 有序数组转平衡二叉树
TreeNode* getTree(vector& nums, int left, int right) {
if (left > right) return nullptr;
int mid = left + ((right - left) / 2);
@@ -76,9 +76,53 @@ public:
# 其他语言版本
Java:
-
+```java
+class Solution {
+ ArrayList res = new ArrayList();
+ // 有序树转成有序数组
+ private void travesal(TreeNode cur) {
+ if (cur == null) return;
+ travesal(cur.left);
+ res.add(cur.val);
+ travesal(cur.right);
+ }
+ // 有序数组转成平衡二叉树
+ private TreeNode getTree(ArrayList nums, int left, int right) {
+ if (left > right) return null;
+ int mid = left + (right - left) / 2;
+ TreeNode root = new TreeNode(nums.get(mid));
+ root.left = getTree(nums, left, mid - 1);
+ root.right = getTree(nums, mid + 1, right);
+ return root;
+ }
+ public TreeNode balanceBST(TreeNode root) {
+ travesal(root);
+ return getTree(res, 0, res.size() - 1);
+ }
+}
+```
Python:
-
+```python
+class Solution:
+ def balanceBST(self, root: TreeNode) -> TreeNode:
+ res = []
+ # 有序树转成有序数组
+ def traversal(cur: TreeNode):
+ if not cur: return
+ traversal(cur.left)
+ res.append(cur.val)
+ traversal(cur.right)
+ # 有序数组转成平衡二叉树
+ def getTree(nums: List, left, right):
+ if left > right: return
+ mid = left + (right -left) // 2
+ root = TreeNode(nums[mid])
+ root.left = getTree(nums, left, mid - 1)
+ root.right = getTree(nums, mid + 1, right)
+ return root
+ traversal(root)
+ return getTree(res, 0, len(res) - 1)
+```
Go:
JavaScript:
@@ -87,6 +131,6 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
index 2b995c59..6897c01f 100644
--- a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
+++ b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
@@ -67,7 +67,7 @@
实现三个函数,时间复杂度分别是 O(n) , O(n^2), O(nlogn),使用加法运算来统一测试。
-```C++
+```CPP
// O(n)
void function1(long long n) {
long long k = 0;
@@ -78,7 +78,7 @@ void function1(long long n) {
```
-```C++
+```CPP
// O(n^2)
void function2(long long n) {
long long k = 0;
@@ -91,7 +91,7 @@ void function2(long long n) {
}
```
-```C++
+```CPP
// O(nlogn)
void function3(long long n) {
long long k = 0;
@@ -105,7 +105,7 @@ void function3(long long n) {
```
来看一下这三个函数随着n的规模变化,耗时会产生多大的变化,先测function1 ,就把 function2 和 function3 注释掉
-```C++
+```CPP
int main() {
long long n; // 数据规模
while (1) {
@@ -154,7 +154,7 @@ O(nlogn)的算法,1s内大概计算机可以运行 2 * (10^7)次计算,符
# 完整测试代码
-```C++
+```CPP
#include
#include
#include
@@ -235,4 +235,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md
index 353bd68e..2eb253ba 100644
--- a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md
+++ b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md
@@ -14,7 +14,7 @@
## 判断子序列
-[动态规划:392.判断子序列](https://mp.weixin.qq.com/s/2pjT4B4fjfOx5iB6N6xyng) 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
+[动态规划:392.判断子序列](https://programmercarl.com/0392.判断子序列.html) 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
这道题目 其实是可以用双指针或者贪心的的,但是我在开篇的时候就说了这是编辑距离的入门题目,因为从题意中我们也可以发现,只需要计算删除的情况,不用考虑增加和替换的情况。
@@ -33,9 +33,9 @@ else dp[i][j] = dp[i][j - 1];
## 不同的子序列
-[动态规划:115.不同的子序列](https://mp.weixin.qq.com/s/1SULY2XVSROtk_hsoVLu8A) 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
+[动态规划:115.不同的子序列](https://programmercarl.com/0115.不同的子序列.html) 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
-本题虽然也只有删除操作,不用考虑替换增加之类的,但相对于[动态规划:392.判断子序列](https://mp.weixin.qq.com/s/2pjT4B4fjfOx5iB6N6xyng)就有难度了,这道题目双指针法可就做不了。
+本题虽然也只有删除操作,不用考虑替换增加之类的,但相对于[动态规划:392.判断子序列](https://programmercarl.com/0392.判断子序列.html)就有难度了,这道题目双指针法可就做不了。
当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。
@@ -58,7 +58,7 @@ else dp[i][j] = dp[i][j - 1];
状态转移方程:
-```C++
+```CPP
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
@@ -68,9 +68,9 @@ if (s[i - 1] == t[j - 1]) {
## 两个字符串的删除操作
-[动态规划:583.两个字符串的删除操作](https://mp.weixin.qq.com/s/a8BerpqSf76DCqkPDJrpYg)给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
+[动态规划:583.两个字符串的删除操作](https://programmercarl.com/0583.两个字符串的删除操作.html)给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
-本题和[动态规划:115.不同的子序列](https://mp.weixin.qq.com/s/1SULY2XVSROtk_hsoVLu8A)相比,其实就是两个字符串可以都可以删除了,情况虽说复杂一些,但整体思路是不变的。
+本题和[动态规划:115.不同的子序列](https://programmercarl.com/0115.不同的子序列.html)相比,其实就是两个字符串可以都可以删除了,情况虽说复杂一些,但整体思路是不变的。
* 当word1[i - 1] 与 word2[j - 1]相同的时候
@@ -89,7 +89,7 @@ if (s[i - 1] == t[j - 1]) {
那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
状态转移方程:
-```C++
+```CPP
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
@@ -100,10 +100,10 @@ if (word1[i - 1] == word2[j - 1]) {
## 编辑距离
-[动态规划:72.编辑距离](https://mp.weixin.qq.com/s/8aG71XjSgZG6kZbiAdkJnQ) 给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
+[动态规划:72.编辑距离](https://programmercarl.com/0072.编辑距离.html) 给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
-编辑距离终于来了,**有了前面三道题目的铺垫,应该有思路了**,本题是两个字符串可以增删改,比 [动态规划:判断子序列](https://mp.weixin.qq.com/s/2pjT4B4fjfOx5iB6N6xyng),[动态规划:不同的子序列](https://mp.weixin.qq.com/s/1SULY2XVSROtk_hsoVLu8A),[动态规划:两个字符串的删除操作](https://mp.weixin.qq.com/s/a8BerpqSf76DCqkPDJrpYg)都要复杂的多。
+编辑距离终于来了,**有了前面三道题目的铺垫,应该有思路了**,本题是两个字符串可以增删改,比 [动态规划:判断子序列](https://programmercarl.com/0392.判断子序列.html),[动态规划:不同的子序列](https://programmercarl.com/0115.不同的子序列.html),[动态规划:两个字符串的删除操作](https://programmercarl.com/0583.两个字符串的删除操作.html)都要复杂的多。
在确定递推公式的时候,首先要考虑清楚编辑的几种操作,整理如下:
@@ -150,7 +150,7 @@ if (word1[i - 1] != word2[j - 1]),此时就需要编辑了,如何编辑呢
递归公式代码如下:
-```C++
+```CPP
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
@@ -161,13 +161,39 @@ else {
## 总结
-心思的录友应该会发现我用了三道题做铺垫,才最后引出了[动态规划:72.编辑距离](https://mp.weixin.qq.com/s/8aG71XjSgZG6kZbiAdkJnQ) ,Carl的良苦用心呀,你们体会到了嘛!
+心思的录友应该会发现我用了三道题做铺垫,才最后引出了[动态规划:72.编辑距离](https://programmercarl.com/0072.编辑距离.html) ,Carl的良苦用心呀,你们体会到了嘛!
## 其他语言版本
Java:
-
+```java
+class Solution {
+ public int minDistance(String word1, String word2) {
+ int m = word1.length();
+ int n = word2.length();
+ int[][] dp = new int[m+1][n+1];
+ for(int i = 1; i <= m; i++){
+ dp[i][0] = i;
+ }
+ for(int i = 1; i <= n; i++){
+ dp[0][i] = i;
+ }
+ for(int i = 1; i <= m; i++){
+ for(int j = 1; j <= n; j++){
+ int left = dp[i][j-1]+1;
+ int mid = dp[i-1][j-1];
+ int right = dp[i-1][j]+1;
+ if(word1.charAt(i-1) != word2.charAt(j-1)){
+ mid ++;
+ }
+ dp[i][j] = Math.min(left,Math.min(mid,right));
+ }
+ }
+ return dp[m][n];
+ }
+}
+```
Python:
@@ -181,4 +207,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树中递归带着回溯.md b/problems/二叉树中递归带着回溯.md
index 372dc40c..7b0ccad7 100644
--- a/problems/二叉树中递归带着回溯.md
+++ b/problems/二叉树中递归带着回溯.md
@@ -11,15 +11,15 @@
> 补充一波
-昨天的总结篇中[还在玩耍的你,该总结啦!(本周小结之二叉树)](https://mp.weixin.qq.com/s/QMBUTYnoaNfsVHlUADEzKg),有两处问题需要说明一波。
+昨天的总结篇中[还在玩耍的你,该总结啦!(本周小结之二叉树)](https://programmercarl.com/周总结/20201003二叉树周末总结.html),有两处问题需要说明一波。
## 求相同的树
-[还在玩耍的你,该总结啦!(本周小结之二叉树)](https://mp.weixin.qq.com/s/QMBUTYnoaNfsVHlUADEzKg)中求100.相同的树的代码中,我笔误贴出了 求对称树的代码了,细心的同学应该都发现了。
+[还在玩耍的你,该总结啦!(本周小结之二叉树)](https://programmercarl.com/周总结/20201003二叉树周末总结.html)中求100.相同的树的代码中,我笔误贴出了 求对称树的代码了,细心的同学应该都发现了。
那么如下我再给出求100. 相同的树 的代码,如下:
-```C++
+```CPP
class Solution {
public:
bool compare(TreeNode* tree1, TreeNode* tree2) {
@@ -42,17 +42,17 @@ public:
};
```
-以上的代码相对于:[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg) 仅仅修改了变量的名字(为了符合判断相同树的语境)和 遍历的顺序。
+以上的代码相对于:[二叉树:我对称么?](https://programmercarl.com/0101.对称二叉树.html) 仅仅修改了变量的名字(为了符合判断相同树的语境)和 遍历的顺序。
-大家应该会体会到:**认清[判断对称树](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)本质之后, 对称树的代码 稍作修改 就可以直接用来AC 100.相同的树。**
+大家应该会体会到:**认清[判断对称树](https://programmercarl.com/0101.对称二叉树.html)本质之后, 对称树的代码 稍作修改 就可以直接用来AC 100.相同的树。**
## 递归中隐藏着回溯
-在[二叉树:找我的所有路径?](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)中我强调了本题其实是用到了回溯的,并且给出了第一个版本的代码,把回溯的过程充分的提现了出来。
+在[二叉树:找我的所有路径?](https://programmercarl.com/0257.二叉树的所有路径.html)中我强调了本题其实是用到了回溯的,并且给出了第一个版本的代码,把回溯的过程充分的提现了出来。
如下的代码充分的体现出回溯:(257. 二叉树的所有路径)
-```C++
+```CPP
class Solution {
private:
@@ -120,13 +120,13 @@ public:
为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
-```
+```CPP
if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
```
改成如下代码:
-```
+```CPP
path += "->";
traversal(cur->left, path, result); // 左
```
@@ -149,7 +149,7 @@ if (cur->right) {
如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。
-```
+```CPP
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
@@ -166,7 +166,7 @@ if (cur->right) {
**大家应该可以感受出来,如果把 `path + "->"`作为函数参数就是可以的,因为并有没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)**
-如果有点遗忘了,建议把这篇[二叉树:找我的所有路径?](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)在仔细看一下,然后再看这里的总结,相信会豁然开朗。
+如果有点遗忘了,建议把这篇[二叉树:找我的所有路径?](https://programmercarl.com/0257.二叉树的所有路径.html)在仔细看一下,然后再看这里的总结,相信会豁然开朗。
这里我尽量把逻辑的每一个细节都抠出来展现了,希望对大家有所帮助!
@@ -448,4 +448,4 @@ func traversal(root *TreeNode,result *[]string,path *[]int){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树总结篇.md b/problems/二叉树总结篇.md
index d4af8aae..ee046366 100644
--- a/problems/二叉树总结篇.md
+++ b/problems/二叉树总结篇.md
@@ -22,105 +22,106 @@
## 二叉树的理论基础
-* [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A):二叉树的种类、存储方式、遍历方式、定义方式
+* [关于二叉树,你该了解这些!](https://programmercarl.com/二叉树理论基础.html):二叉树的种类、存储方式、遍历方式、定义方式
## 二叉树的遍历方式
* 深度优先遍历
- * [二叉树:前中后序递归法](https://mp.weixin.qq.com/s/PwVIfxDlT3kRgMASWAMGhA):递归三部曲初次亮相
- * [二叉树:前中后序迭代法(一)](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg):通过栈模拟递归
- * [二叉树:前中后序迭代法(二)统一风格](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)
+ * [二叉树:前中后序递归法](https://programmercarl.com/二叉树的递归遍历.html):递归三部曲初次亮相
+ * [二叉树:前中后序迭代法(一)](https://programmercarl.com/二叉树的迭代遍历.html):通过栈模拟递归
+ * [二叉树:前中后序迭代法(二)统一风格](https://programmercarl.com/二叉树的统一迭代法.html)
* 广度优先遍历
- * [二叉树的层序遍历](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog):通过队列模拟
+ * [二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html):通过队列模拟
## 求二叉树的属性
-* [二叉树:是否对称](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)
+* [二叉树:是否对称](https://programmercarl.com/0101.对称二叉树.html)
* 递归:后序,比较的是根节点的左子树与右子树是不是相互翻转
* 迭代:使用队列/栈将两个节点顺序放入容器中进行比较
-* [二叉树:求最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)
+* [二叉树:求最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)
* 递归:后序,求根节点最大高度就是最大深度,通过递归函数的返回值做计算树的高度
* 迭代:层序遍历
-* [二叉树:求最小深度](https://mp.weixin.qq.com/s/BH8-gPC3_QlqICDg7rGSGA)
+* [二叉树:求最小深度](https://programmercarl.com/0111.二叉树的最小深度.html)
* 递归:后序,求根节点最小高度就是最小深度,注意最小深度的定义
* 迭代:层序遍历
-* [二叉树:求有多少个节点](https://mp.weixin.qq.com/s/2_eAjzw-D0va9y4RJgSmXw)
+* [二叉树:求有多少个节点](https://programmercarl.com/0222.完全二叉树的节点个数.html)
* 递归:后序,通过递归函数的返回值计算节点数量
* 迭代:层序遍历
-* [二叉树:是否平衡](https://mp.weixin.qq.com/s/isUS-0HDYknmC0Rr4R8mww)
+* [二叉树:是否平衡](https://programmercarl.com/0110.平衡二叉树.html)
* 递归:后序,注意后序求高度和前序求深度,递归过程判断高度差
* 迭代:效率很低,不推荐
-* [二叉树:找所有路径](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)
+* [二叉树:找所有路径](https://programmercarl.com/0257.二叉树的所有路径.html)
* 递归:前序,方便让父节点指向子节点,涉及回溯处理根节点到叶子的所有路径
* 迭代:一个栈模拟递归,一个栈来存放对应的遍历路径
-* [二叉树:递归中如何隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA)
- * 详解[二叉树:找所有路径](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)中递归如何隐藏着回溯
-* [二叉树:求左叶子之和](https://mp.weixin.qq.com/s/gBAgmmFielojU5Wx3wqFTA)
+* [二叉树:递归中如何隐藏着回溯](https://programmercarl.com/二叉树中递归带着回溯.html)
+ * 详解[二叉树:找所有路径](https://programmercarl.com/0257.二叉树的所有路径.html)中递归如何隐藏着回溯
+* [二叉树:求左叶子之和](https://programmercarl.com/0404.左叶子之和.html)
* 递归:后序,必须三层约束条件,才能判断是否是左叶子。
* 迭代:直接模拟后序遍历
-* [二叉树:求左下角的值](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)
+* [二叉树:求左下角的值](https://programmercarl.com/0513.找树左下角的值.html)
* 递归:顺序无所谓,优先左孩子搜索,同时找深度最大的叶子节点。
* 迭代:层序遍历找最后一行最左边
-* [二叉树:求路径总和](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg)
+* [二叉树:求路径总和](https://programmercarl.com/0112.路径总和.html)
* 递归:顺序无所谓,递归函数返回值为bool类型是为了搜索一条边,没有返回值是搜索整棵树。
* 迭代:栈里元素不仅要记录节点指针,还要记录从头结点到该节点的路径数值总和
## 二叉树的修改与构造
-* [翻转二叉树](https://mp.weixin.qq.com/s/6gY1MiXrnm-khAAJiIb5Bg)
+* [翻转二叉树](https://programmercarl.com/0226.翻转二叉树.html)
* 递归:前序,交换左右孩子
* 迭代:直接模拟前序遍历
-* [构造二叉树](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)
+* [构造二叉树](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html)
* 递归:前序,重点在于找分割点,分左右区间构造
* 迭代:比较复杂,意义不大
-* [构造最大的二叉树](https://mp.weixin.qq.com/s/1iWJV6Aov23A7xCF4nV88w)
+* [构造最大的二叉树](https://programmercarl.com/0654.最大二叉树.html)
* 递归:前序,分割点为数组最大值,分左右区间构造
* 迭代:比较复杂,意义不大
-* [合并两个二叉树](https://mp.weixin.qq.com/s/3f5fbjOFaOX_4MXzZ97LsQ)
+* [合并两个二叉树](https://programmercarl.com/0617.合并二叉树.html)
* 递归:前序,同时操作两个树的节点,注意合并的规则
* 迭代:使用队列,类似层序遍历
## 求二叉搜索树的属性
-* [二叉搜索树中的搜索](https://mp.weixin.qq.com/s/vsKrWRlETxCVsiRr8v_hHg)
+* [二叉搜索树中的搜索](https://programmercarl.com/0700.二叉搜索树中的搜索.html)
* 递归:二叉搜索树的递归是有方向的
* 迭代:因为有方向,所以迭代法很简单
-* [是不是二叉搜索树](https://mp.weixin.qq.com/s/8odY9iUX5eSi0eRFSXFD4Q)
+* [是不是二叉搜索树](https://programmercarl.com/0098.验证二叉搜索树.html)
* 递归:中序,相当于变成了判断一个序列是不是递增的
* 迭代:模拟中序,逻辑相同
-* [求二叉搜索树的最小绝对差](https://mp.weixin.qq.com/s/Hwzml6698uP3qQCC1ctUQQ)
+* [求二叉搜索树的最小绝对差](https://programmercarl.com/0530.二叉搜索树的最小绝对差.html)
* 递归:中序,双指针操作
* 迭代:模拟中序,逻辑相同
-* [求二叉搜索树的众数](https://mp.weixin.qq.com/s/KSAr6OVQIMC-uZ8MEAnGHg)
+* [求二叉搜索树的众数](https://programmercarl.com/0501.二叉搜索树中的众数.html)
+
* 递归:中序,清空结果集的技巧,遍历一遍便可求众数集合
- * 迭代:模拟中序,逻辑相同
-* [二叉搜索树转成累加树](https://mp.weixin.qq.com/s/hZtJh4T5lIGBarY-lZJf6Q)
+ * [二叉搜索树转成累加树](https://programmercarl.com/0538.把二叉搜索树转换为累加树.html)
+
* 递归:中序,双指针操作累加
* 迭代:模拟中序,逻辑相同
## 二叉树公共祖先问题
-* [二叉树的公共祖先问题](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)
+* [二叉树的公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)
* 递归:后序,回溯,找到左子树出现目标值,右子树节点目标值的节点。
* 迭代:不适合模拟回溯
-* [二叉搜索树的公共祖先问题](https://mp.weixin.qq.com/s/Ja9dVw2QhBcg_vV-1fkiCg)
+* [二叉搜索树的公共祖先问题](https://programmercarl.com/0235.二叉搜索树的最近公共祖先.html)
* 递归:顺序无所谓,如果节点的数值在目标区间就是最近公共祖先
* 迭代:按序遍历
## 二叉搜索树的修改与构造
-* [二叉搜索树中的插入操作](https://mp.weixin.qq.com/s/lwKkLQcfbCNX2W-5SOeZEA)
+* [二叉搜索树中的插入操作](https://programmercarl.com/0701.二叉搜索树中的插入操作.html)
* 递归:顺序无所谓,通过递归函数返回值添加节点
* 迭代:按序遍历,需要记录插入父节点,这样才能做插入操作
-* [二叉搜索树中的删除操作](https://mp.weixin.qq.com/s/-p-Txvch1FFk3ygKLjPAKw)
+* [二叉搜索树中的删除操作](https://programmercarl.com/0450.删除二叉搜索树中的节点.html)
* 递归:前序,想清楚删除非叶子节点的情况
* 迭代:有序遍历,较复杂
-* [修剪二叉搜索树](https://mp.weixin.qq.com/s/QzmGfYUMUWGkbRj7-ozHoQ)
+* [修剪二叉搜索树](https://programmercarl.com/0669.修剪二叉搜索树.html)
* 递归:前序,通过递归函数返回值删除节点
* 迭代:有序遍历,较复杂
-* [构造二叉搜索树](https://mp.weixin.qq.com/s/sy3ygnouaZVJs8lhFgl9mw)
+* [构造二叉搜索树](https://programmercarl.com/0108.将有序数组转换为二叉搜索树.html)
* 递归:前序,数组中间节点分割
* 迭代:较复杂,通过三个队列来模拟
@@ -130,10 +131,10 @@
**每周小结都会对大家的疑问做统一解答,并且对每周的内容进行拓展和补充,所以一定要看,将细碎知识点一网打尽!**
-* [本周小结!(二叉树系列一)](https://mp.weixin.qq.com/s/JWmTeC7aKbBfGx4TY6uwuQ)
-* [本周小结!(二叉树系列二)](https://mp.weixin.qq.com/s/QMBUTYnoaNfsVHlUADEzKg)
-* [本周小结!(二叉树系列三)](https://mp.weixin.qq.com/s/JLLpx3a_8jurXcz6ovgxtg)
-* [本周小结!(二叉树系列四)](https://mp.weixin.qq.com/s/CbdtOTP0N-HIP7DR203tSg)
+* [本周小结!(二叉树系列一)](https://programmercarl.com/周总结/20200927二叉树周末总结.html)
+* [本周小结!(二叉树系列二)](https://programmercarl.com/周总结/20201003二叉树周末总结.html)
+* [本周小结!(二叉树系列三)](https://programmercarl.com/周总结/20201010二叉树周末总结.html)
+* [本周小结!(二叉树系列四)](https://programmercarl.com/周总结/20201017二叉树周末总结.html)
## 最后总结
@@ -145,7 +146,7 @@
* 求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。
-注意在普通二叉树的属性中,我用的是一般为后序,例如单纯求深度就用前序, [二叉树:找所有路径](https://mp.weixin.qq.com/s/Osw4LQD2xVUnCJ-9jrYxJA)也用了前序,这是为了方便让父节点指向子节点。
+注意在普通二叉树的属性中,我用的是一般为后序,例如单纯求深度就用前序,[二叉树:找所有路径](https://programmercarl.com/0257.二叉树的所有路径.html)也用了前序,这是为了方便让父节点指向子节点。
所以求普通二叉树的属性还是要具体问题具体分析。
@@ -174,4 +175,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md
index a720672e..94a0d67f 100644
--- a/problems/二叉树理论基础.md
+++ b/problems/二叉树理论基础.md
@@ -215,7 +215,7 @@ class TreeNode:
```
Go:
-```
+```go
type TreeNode struct {
Val int
Left *TreeNode
@@ -223,10 +223,18 @@ type TreeNode struct {
}
```
+JavaScript:
+```javascript
+function TreeNode(val, left, right) {
+ this.val = (val===undefined ? 0 : val)
+ this.left = (left===undefined ? null : left)
+ this.right = (right===undefined ? null : right)
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树的统一迭代法.md b/problems/二叉树的统一迭代法.md
index 9e24fef6..d8299e10 100644
--- a/problems/二叉树的统一迭代法.md
+++ b/problems/二叉树的统一迭代法.md
@@ -12,9 +12,9 @@
> 统一写法是一种什么感觉
-此时我们在[二叉树:一入递归深似海,从此offer是路人](https://mp.weixin.qq.com/s/Ww60X5mIKWdMQV4cN3ejOA)中用递归的方式,实现了二叉树前中后序的遍历。
+此时我们在[二叉树:一入递归深似海,从此offer是路人](https://programmercarl.com/二叉树的递归遍历.html)中用递归的方式,实现了二叉树前中后序的遍历。
-在[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)中用栈实现了二叉树前后中序的迭代遍历(非递归)。
+在[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)中用栈实现了二叉树前后中序的迭代遍历(非递归)。
之后我们发现**迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。**
@@ -24,7 +24,7 @@
**重头戏来了,接下来介绍一下统一写法。**
-我们以中序遍历为例,在[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)中提到说使用栈的话,**无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况**。
+我们以中序遍历为例,在[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)中提到说使用栈的话,**无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况**。
**那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。**
@@ -34,7 +34,7 @@
中序遍历代码如下:(详细注释)
-```C++
+```CPP
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
@@ -77,7 +77,7 @@ public:
迭代法前序遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**)
-```C++
+```CPP
class Solution {
public:
vector preorderTraversal(TreeNode* root) {
@@ -108,7 +108,7 @@ public:
后续遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**)
-```C++
+```CPP
class Solution {
public:
vector postorderTraversal(TreeNode* root) {
@@ -530,4 +530,4 @@ var postorderTraversal = function(root, res = []) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md
index 20397773..84363610 100644
--- a/problems/二叉树的迭代遍历.md
+++ b/problems/二叉树的迭代遍历.md
@@ -19,7 +19,7 @@
为什么可以用迭代法(非递归的方式)来实现二叉树的前后中序遍历呢?
-我们在[栈与队列:匹配问题都是栈的强项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)中提到了,**递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中**,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
+我们在[栈与队列:匹配问题都是栈的强项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)中提到了,**递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中**,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
此时大家应该知道我们用栈也可以是实现二叉树的前后中序遍历了。
@@ -27,7 +27,7 @@
我们先看一下前序遍历。
-前序遍历是中左右,每次先处理的是中间节点,那么先将跟节点放入栈中,然后将右孩子加入栈,再加入左孩子。
+前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。
为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。
@@ -37,7 +37,7 @@
不难写出如下代码: (**注意代码中空节点不入栈**)
-```C++
+```CPP
class Solution {
public:
vector preorderTraversal(TreeNode* root) {
@@ -84,7 +84,7 @@ public:
**中序遍历,可以写出如下代码:**
-```C++
+```CPP
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
@@ -116,7 +116,7 @@ public:
**所以后序遍历只需要前序遍历的代码稍作修改就可以了,代码如下:**
-```C++
+```CPP
class Solution {
public:
vector postorderTraversal(TreeNode* root) {
@@ -140,7 +140,7 @@ public:
# 总结
-此时我们用迭代法写出了二叉树的前后中序遍历,大家可以看出前序和中序是完全两种代码风格,并不想递归写法那样代码稍做调整,就可以实现前后中序。
+此时我们用迭代法写出了二叉树的前后中序遍历,大家可以看出前序和中序是完全两种代码风格,并不像递归写法那样代码稍做调整,就可以实现前后中序。
**这是因为前序遍历中访问节点(遍历节点)和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!**
@@ -473,4 +473,4 @@ var postorderTraversal = function(root, res = []) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树的递归遍历.md b/problems/二叉树的递归遍历.md
index 2b5a44dd..93f4000e 100644
--- a/problems/二叉树的递归遍历.md
+++ b/problems/二叉树的递归遍历.md
@@ -58,7 +58,7 @@ traversal(cur->right, vec); // 右
前序遍历:
-```C++
+```CPP
class Solution {
public:
void traversal(TreeNode* cur, vector