Merge branch 'master' into remote

This commit is contained in:
程序员Carl
2021-10-25 12:48:10 +08:00
committed by GitHub
233 changed files with 8680 additions and 1537 deletions

View File

@ -88,6 +88,7 @@
* 编程语言
* [C++面试&C++学习指南知识点整理](https://github.com/youngyangyang04/TechCPP)
* 项目
* [基于跳表的轻量级KV存储引擎](https://github.com/youngyangyang04/Skiplist-CPP)
* [Nosql数据库注入攻击系统](https://github.com/youngyangyang04/NoSQLAttack)
@ -96,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)
@ -120,30 +122,29 @@
* [递归算法的时间与空间复杂度分析!](./problems/前序/递归算法的时间与空间复杂度分析.md)
* [刷了这么多题,你了解自己代码的内存消耗么?](./problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md)
## 社群
* [准备秋招的录友们,组织在这里!](https://mp.weixin.qq.com/s/xX4LFwZQIG_XiQAxVBrfeA)
* [准备社招的录友们,组织在这里!](https://mp.weixin.qq.com/s/mbQ3s17ZJ4LXFRb-VD58Ww)
## 知识星球精选
* [HR面注意事项](https://mp.weixin.qq.com/s/0mDyCyCBfa0DeGov3Pebnw)
* [刷题攻略要刷两遍!](https://mp.weixin.qq.com/s/e3_L7FZglY4UlTVvKolZyQ)
* [秋招进行中的迷茫与焦虑......](https://mp.weixin.qq.com/s/X15MUw4sfH_AQNHdAivEvg)
* [大厂新人培养体系应该是什么样的?](https://mp.weixin.qq.com/s/WBaPCosOljB5NEkFL2GhOQ)
* [你的简历里「专业技能」写的够专业么?](https://mp.weixin.qq.com/s/bp6y-e5FVN28H9qc8J9zrg)
* [Carl看了上百份简历总结了这些](https://mp.weixin.qq.com/s/sJa87MZD28piCOVMFkIbwQ)
* [备战2022届秋招](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA)
* [技术不太好,如果选择方向](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g)
* [刷题要不要使用库函数](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg)
* [关于实习的几点问题](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA)
* [面试中遇到了发散性问题,怎么帮?](https://mp.weixin.qq.com/s/SSonDxi2pjkSVwHNzZswng)
* [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw)
* [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg)
* [关于提前批的一些建议](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ)
* [已经在实习的录友要如何准备秋招](https://mp.weixin.qq.com/s/ka07IPryFnfmIjByFFcXDg)
* [华为提前批已经开始了](https://mp.weixin.qq.com/s/OC35QDG8pn5OwLpCxieStw)
* [秋招下半场依然没offer怎么办](./problems/知识星球精选/秋招下半场依然没offer.md)
* [合适自己的就是最好的](./problems/知识星球精选/合适自己的就是最好的.md)
* [为什么都说客户端会消失](./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)
## 杂谈
@ -153,7 +154,6 @@
* [大半年过去了......](https://mp.weixin.qq.com/s/lubfeistPxBLSQIe5XYg5g)
* [一万录友在B站学算法](https://mp.weixin.qq.com/s/Vzq4zkMZY7erKeu0fqGLgw)
## 数组
1. [数组过于简单,但你该了解这些!](./problems/数组理论基础.md)
@ -304,7 +304,8 @@
题目分类大纲如下:
<img src='https://img-blog.csdnimg.cn/20210220152245584.png' width=600 alt='贪心算法大纲'> </img></div>
<img src='https://code-thinking-1253855093.file.myqcloud.com/pics/20210917104315.png' width=600 alt='贪心算法大纲'> </img></div>
1. [关于贪心算法,你该了解这些!](./problems/贪心算法理论基础.md)
2. [贪心算法:分发饼干](./problems/0455.分发饼干.md)
@ -457,6 +458,7 @@
* [724.寻找数组的中心索引](./problems/0724.寻找数组的中心索引.md)
* [34.在排序数组中查找元素的第一个和最后一个位置](./problems/0034.在排序数组中查找元素的第一个和最后一个位置.md) (二分法)
* [922.按奇偶排序数组II](./problems/0922.按奇偶排序数组II.md)
* [35.搜索插入位置](./problems/0035.搜索插入位置.md)
## 链表
@ -485,6 +487,7 @@
## 贪心
* [649.Dota2参议院](./problems/0649.Dota2参议院.md) 有难度
* [1221.分割平衡字符](./problems/1221.分割平衡字符串.md) 简单贪心
## 动态规划
* [5.最长回文子串](./problems/0005.最长回文子串.md) 和[647.回文子串](https://mp.weixin.qq.com/s/2WetyP6IYQ6VotegepVpEw) 差不多是一样的

View File

@ -253,4 +253,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -8,6 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 5.最长回文子串
[力扣题目链接](https://leetcode-cn.com/problems/longest-palindromic-substring/)
@ -288,7 +288,34 @@ class Solution:
return s[left:right + 1]
```
> 双指针法:
```python
class Solution:
def longestPalindrome(self, s: str) -> str:
def find_point(i, j, s):
while i >= 0 and j < len(s) and s[i] == s[j]:
i -= 1
j += 1
return i + 1, j
def compare(start, end, left, right):
if right - left > end - start:
return left, right
else:
return start, end
start = 0
end = 0
for i in range(len(s)):
left, right = find_point(i, i, s)
start, end = compare(start, end, left, right)
left, right = find_point(i, i + 1, s)
start, end = compare(start, end, left, right)
return s[start:end]
```
## Go
```go
@ -297,12 +324,123 @@ class Solution:
## JavaScript
```js
//动态规划解法
var longestPalindrome = function(s) {
const len = s.length;
// 布尔类型的dp[i][j]:表示区间范围[i,j] 注意是左闭右闭的子串是否是回文子串如果是dp[i][j]为true否则为false
let dp = new Array(len).fill(false).map(() => new Array(len).fill(false));
// left起始位置 maxlenth回文串长度
let left = 0, maxlenth = 0;
for(let i = len - 1; i >= 0; i--){
for(let j = i; j < len; j++){
// 情况一下标i 与 j相同同一个字符例如a当然是回文子串 j - i == 0
// 情况二下标i 与 j相差为1例如aa也是文子串 j - i == 1
// 情况一和情况二 可以合并为 j - i <= 1
// 情况三下标i 与 j相差大于1的时候例如cabac此时s[i]与s[j]已经相同了我们看i到j区间是不是回文子串就看aba是不是回文就可以了那么aba的区间就是 i+1 与 j-1区间这个区间是不是回文就看dp[i + 1][j - 1]===true
if(s[i] === s[j] && (j - i <= 1 || dp[i + 1][j - 1])){
dp[i][j] = true;
}
// 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
if(dp[i][j] && j - i + 1 > maxlenth) {
maxlenth = j - i + 1; // 回文串长度
left = i; // 起始位置
}
}
}
return s.substr(left, maxlenth); // 找到子串
};
//双指针
var longestPalindrome = function(s) {
let left = 0, right = 0, maxLength = 0;
const extend = (s, i, j, n) => {// s为字符串 i,j为双指针 n为字符串长度
while(i >= 0 && j < n && s[i] === s[j]){
if(j - i + 1 > maxLength){
left = i; // 更新开始位置
right = j; // 更新结尾位置
maxLength = j - i + 1; // 更新子串最大长度
}
// 指针移动
i--;
j++;
}
}
for(let i = 0; i < s.length; i++){
extend(s, i, i, s.length); // 以i为中心
extend(s, i, i + 1, s.length); // 以i和i+1为中心
}
return s.substr(left, maxLength);
};
//Manacher算法
var longestPalindrome = function(s) {
const len = s.length;
if(len < 2) return s;
let maxLength = 1, index = 0;
//Manacher算法,利用回文对称的性质根据i在上一个回文中心的臂长里的位置去判断i的回文性
//需要知道上一个回文中心,以及其臂长
let center = 0;
//注意这里使用了maxRight的而不是真实的臂长length,因为之后需要判断i在臂长的什么位置
//如果这里臂长用了length,之后还要 计算i - center 去和 length比较太繁琐
let maxRight = 0;
//考虑到回文串的长度是偶数的情况,所以这里预处理一下字符串,每个字符间插入特殊字符,把可能性都化为奇数
//这个处理把回文串长度的可能性都化为了奇数
//#c#b#b#a#
//#c#b#a#b#d#
let ss = "";
for(let i = 0; i < s.length; i++){
ss += "#"+s[i];
}
ss += "#";
//需要维护一个每个位置臂长的信息数组positionLength
const pl = new Array(ss.length).fill(0);
//这里需要注意参考的是i关于center对称的点i'的回文性
//i' = 2*center - i;
//所以列下情况:
//1.i>maxRight,找不到i',无法参考,自己算自己的
//2.i<=maxRight:
//2.1 i<maxRight-pl[i'],pl[i']的臂长没有超过center的臂长,根据对称性,pl[i] = pl[i']
//2.2 i=maxRight-pl[i'],pl[i']的臂长刚好等于center的臂长,根据对称性,pl[i] >= pl[i],大多少需要尝试扩散
//2.3 i>maxRight-pl[i'],pl[i']的臂长超过了center的臂长,根据对称性,i中心扩散到MaxRight处,
// s[2*i-maxRight] !== s[MaxRight]必不相等所以pl[i] = maxRight-i;
//总结就是pl[i] = Math.min(maxRight-i,pl[i']);提示i<maxRight-pl[i'] 也可写成 pl[i']<maxRight-i
//0没有意义,从1开始计算
for(let i = 1; i < ss.length; i++){
if(i <= maxRight){//可以参考之前的
pl[i] = Math.min(maxRight - i, pl[2 * center - i]);
//尝试中心扩散
}
//注意到i<maxRight时都要尝试中心扩散,所以写else完全无意义,把中心扩散的代码写在下面
// else{//i不在之前回文中心的臂长范围里,之前的信息就完全无法参考,只能从i中心扩散把然后去维护maxRight和center的定义
//尝试中心扩散
//这里不要动center和maxRight
// center = i;
// maxRight = pl[i] + i + 1;
let right = pl[i] + i + 1;
let left = i - pl[i] - 1;
while (left >= 0 && right<ss.length && ss[left] === ss[right]) {
right++;
left--;
pl[i]++;
}
// }
if(pl[i] + i > maxRight){
center = i;
maxRight = pl[i] + i;
}
if (pl[i] * 2 + 1 > maxLength){
maxLength = pl[i]*2+1;
index = i - pl[i];
}
}
return ss.substr(index, maxLength).replace(/#/g,"");
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -354,44 +354,6 @@ def is_valid(strs)
end
```
php:
```php
function threeSum(array $nums): array
{
$result = [];
$length = count($nums);
if ($length < 3) {
return [];
}
sort($nums);
for ($i = 0; $i < $length; $i++) {
// 如果大于0结束
if ($nums[$i] > 0) break;
// 去重
if ($i > 0 && $nums[$i] == $nums[$i - 1]) continue;
$left = $i + 1;
$right = $length - 1;
// 比较
while ($left < $right) {
$sum = $nums[$i] + $nums[$left] + $nums[$right];
if ($sum < 0) {
$left++;
} elseif ($sum > 0) {
$right--;
} else {
array_push($result, [$nums[$i], $nums[$left], $nums[$right]]);
while ($left < $right && $nums[$left] == $nums[$left + 1]) $left++;
while ($left < $right && $nums[$right - 1] == $nums[$right]) $right--;
$left++;
$right--;
}
}
}
return $result;
}
```
PHP:
```php
@ -434,9 +396,49 @@ class Solution {
}
```
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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -29,7 +29,7 @@
如果输入"233"呢那么就三层for循环如果"2333"呢就四层for循环.......
大家应该感觉出和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)遇到的一样的问题就是这for循环的层数如何写出来此时又是回溯法登场的时候了。
大家应该感觉出和[77.组合](https://programmercarl.com/0077.组合.html)遇到的一样的问题就是这for循环的层数如何写出来此时又是回溯法登场的时候了。
理解本题后,要解决如下三个问题:
@ -75,7 +75,7 @@ const string letterMap[10] = {
再来看参数参数指定是有题目中给的string digits然后还要有一个参数就是int型的index。
注意这个index可不是 [回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:求组合总和](https://programmercarl.com/0216.组合总和III.html)中的startIndex了。
注意这个index可不是 [77.组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)中的startIndex了。
这个index是记录遍历第几个数字了就是用来遍历digits的题目中给出数字字符串同时index也表示树的深度。
@ -110,7 +110,7 @@ if (index == digits.size()) {
然后for循环来处理这个字符集代码如下
```
```CPP
int digit = digits[index] - '0'; // 将index指向的数字转为int
string letters = letterMap[digit]; // 取数字对应的字符集
for (int i = 0; i < letters.size(); i++) {
@ -137,7 +137,7 @@ for (int i = 0; i < letters.size(); i++) {
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中的回溯法模板不难写出如下C++代码:
```c++
```CPP
// 版本一
class Solution {
private:
@ -183,7 +183,7 @@ public:
一些写法,是把回溯的过程放在递归函数里了,例如如下代码,我可以写成这样:(注意注释中不一样的地方)
```c++
```CPP
// 版本二
class Solution {
private:
@ -236,10 +236,10 @@ public:
## 其他语言版本
# 其他语言版本
Java
## Java
```Java
class Solution {
@ -281,69 +281,81 @@ class Solution {
}
```
Python
```Python
## Python
**回溯**
```python3
class Solution:
ans = []
s = ''
letterMap = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz'
}
def __init__(self):
self.answers: List[str] = []
self.answer: str = ''
self.letter_map = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz'
}
def letterCombinations(self, digits):
self.ans.clear()
if digits == '':
return self.ans
self.backtracking(digits, 0)
return self.ans
def backtracking(self, digits, index):
if index == len(digits):
self.ans.append(self.s)
return
else:
letters = self.letterMap[digits[index]] # 取出数字对应的字符集
for letter in letters:
self.s = self.s + letter # 处理
self.backtracking(digits, index + 1)
self.s = self.s[:-1] # 回溯
```
python3
```py
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
self.s = ""
res = []
letterMap = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
if len(digits) == 0: return res
def backtrack(digits,index):
if index == len(digits):
return res.append(self.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)
return res
self.answers.clear()
if not digits: return []
self.backtracking(digits, 0)
return self.answers
def backtracking(self, digits: str, index: int) -> None:
# 回溯函数没有返回值
# Base Case
if index == len(digits): # 当遍历穷尽后的下一层时
self.answers.append(self.answer)
return
# 单层递归逻辑
letters: str = self.letter_map[digits[index]]
for letter in letters:
self.answer += letter # 处理
self.backtracking(digits, index + 1) # 递归至下一层
self.answer = self.answer[:-1] # 回溯
```
**回溯简化**
```python3
class Solution:
def __init__(self):
self.answers: List[str] = []
self.letter_map = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz'
}
def letterCombinations(self, digits: str) -> List[str]:
self.answers.clear()
if not digits: return []
self.backtracking(digits, 0, '')
return self.answers
def backtracking(self, digits: str, index: int, answer: str) -> None:
# 回溯函数没有返回值
# Base Case
if index == len(digits): # 当遍历穷尽后的下一层时
self.answers.append(answer)
return
# 单层递归逻辑
letters: str = self.letter_map[digits[index]]
for letter in letters:
self.backtracking(digits, index + 1, answer + letter) # 递归至下一层 + 回溯
```
Go
## Go
> 主要在于递归中传递下一个数字
主要在于递归中传递下一个数字
```go
func letterCombinations(digits string) []string {
@ -382,7 +394,7 @@ func recursion(tempString ,digits string, Index int,digitsMap [10]string, res *[
}
```
javaScript
## javaScript
```js
var letterCombinations = function(digits) {
@ -476,4 +488,4 @@ char ** letterCombinations(char * digits, int* returnSize){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -90,7 +90,8 @@ public:
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
if (nums[k] + nums[i] + nums[left] + nums[right] > target) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if (nums[k] + nums[i] > target - (nums[left] + nums[right])) {
right--;
} else if (nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
@ -110,8 +111,8 @@ public:
}
return result;
}
};
```
@ -354,8 +355,56 @@ class Solution {
}
```
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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -50,7 +50,7 @@
* fast首先走n + 1步 为什么是n+1呢因为只有这样同时移动的时候slow才能指向删除节点的上一个节点方便做删除操作如图
<img src='https://code-thinking.cdn.bcebos.com/pics/19.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B91.png' width=600> </img></div>
* fast和slow同时移动之道fast指向末尾如题
* fast和slow同时移动直到fast指向末尾如题
<img src='https://code-thinking.cdn.bcebos.com/pics/19.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B92.png' width=600> </img></div>
* 删除slow指向的下一个节点如图
@ -233,4 +233,4 @@ func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -290,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -283,4 +283,4 @@ func swapPairs(_ head: ListNode?) -> ListNode? {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -185,7 +185,7 @@ func removeElement(nums []int, val int) int {
```
JavaScript:
```
```javascript
//时间复杂度O(n)
//空间复杂度O(1)
var removeElement = (nums, val) => {
@ -290,4 +290,4 @@ int removeElement(int* nums, int numsSize, int val){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -215,7 +215,7 @@ next数组就可以是前缀表但是很多实现都是把前缀表统一减
其实**这并不涉及到KMP的原理而是具体实现next数组即可以就是前缀表也可以是前缀表统一减一右移一位初始位置为-1。**
后面我会提供两种不同的实现代码,大家就明白了
后面我会提供两种不同的实现代码,大家就明白了。
# 使用next数组来匹配
@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -9,6 +8,7 @@
# 31.下一个排列
[力扣题目链接](https://leetcode-cn.com/problems/next-permutation/)
@ -132,11 +132,57 @@ class Solution {
## JavaScript
```js
//卡尔的解法(吐槽一下JavaScript的sort和其他语言的不太一样只想到了拷贝数组去排序再替换原数组来实现nums的[i + 1, nums.length)升序排序)
var nextPermutation = function(nums) {
for(let i = nums.length - 1; i >= 0; i--){
for(let j = nums.length - 1; j > i; j--){
if(nums[j] > nums[i]){
[nums[j],nums[i]] = [nums[i],nums[j]]; // 交换
// 深拷贝[i + 1, nums.length)部分到新数组arr
let arr = nums.slice(i+1);
// arr升序排序
arr.sort((a,b) => a - b);
// arr替换nums的[i + 1, nums.length)部分
nums.splice(i+1,nums.length - i, ...arr);
return;
}
}
}
nums.sort((a,b) => a - b); // 不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
};
//另一种
var nextPermutation = function(nums) {
let i = nums.length - 2;
// 从右往左遍历拿到第一个左边小于右边的 i,此时 i 右边的数组是从右往左递增的
while (i >= 0 && nums[i] >= nums[i+1]){
i--;
}
if (i >= 0){
let j = nums.length - 1;
// 从右往左遍历拿到第一个大于nums[i]的数,因为之前nums[i]是第一个小于他右边的数,所以他的右边一定有大于他的数
while (j >= 0 && nums[j] <= nums[i]){
j--;
}
// 交换两个数
[nums[j], nums[i]] = [nums[i], nums[j]];
}
// 对 i 右边的数进行交换
// 因为 i 右边的数原来是从右往左递增的,把一个较小的值交换过来之后,仍然维持单调递增特性
// 此时头尾交换并向中间逼近就能获得 i 右边序列的最小值
let l = i + 1;
let r = nums.length - 1;
while (l < r){
[nums[l], nums[r]] = [nums[r], nums[l]];
l++;
r--;
}
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
@ -396,11 +396,51 @@ class Solution:
## JavaScript
```js
var searchRange = function(nums, target) {
const getLeftBorder = (nums, target) => {
let left = 0, right = nums.length - 1;
let leftBorder = -2;// 记录一下leftBorder没有被赋值的情况
while(left <= right){
let middle = left + ((right - left) >> 1);
if(nums[middle] >= target){ // 寻找左边界nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
const getRightBorder = (nums, target) => {
let left = 0, right = nums.length - 1;
let rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) {
let middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle - 1;
} else { // 寻找右边界nums[middle] == target的时候更新left
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
let leftBorder = getLeftBorder(nums, target);
let rightBorder = getRightBorder(nums, target);
// 情况一
if(leftBorder === -2 || rightBorder === -2) return [-1,-1];
// 情况三
if (rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1];
// 情况二
return [-1, -1];
};
```
-----------------------
* 作者微信[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -232,7 +232,24 @@ class Solution {
}
}
```
Golang:
```golang
// 第一种二分法
func searchInsert(nums []int, target int) int {
l, r := 0, len(nums) - 1
for l <= r{
m := l + (r - l)/2
if nums[m] == target{
return m
}else if nums[m] > target{
r = m - 1
}else{
l = m + 1
}
}
return r + 1
}
```
Python
```python3
@ -313,4 +330,4 @@ func searchInsert(_ nums: [Int], _ target: Int) -> Int {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -293,89 +293,99 @@ class Solution:
"""
Do not return anything, modify board in-place instead.
"""
def backtrack(board):
for i in range(len(board)): #遍历行
for j in range(len(board[0])): #遍历列
if board[i][j] != ".": continue
for k in range(1,10): #(i, j) 这个位置放k是否合适
if isValid(i,j,k,board):
board[i][j] = str(k) #放置k
if backtrack(board): return True #如果找到合适一组立刻返回
board[i][j] = "." #回溯撤销k
return False #9个数都试完了都不行那么就返回false
return True #遍历完没有返回false说明找到了合适棋盘位置了
def isValid(row,col,val,board):
for i in range(9): #判断行里是否重复
if board[row][i] == str(val):
self.backtracking(board)
def backtracking(self, board: List[List[str]]) -> bool:
# 若有解返回True若无解返回False
for i in range(len(board)): # 遍历行
for j in range(len(board[0])): # 遍历列
# 若空格内已有数字,跳过
if board[i][j] != '.': continue
for k in range(1, 10):
if self.is_valid(i, j, k, board):
board[i][j] = str(k)
if self.backtracking(board): return True
board[i][j] = '.'
# 若数字1-9都不能成功填入空格返回False无解
return False
return True # 有解
def is_valid(self, row: int, col: int, val: int, board: List[List[str]]) -> bool:
# 判断同一行是否冲突
for i in range(9):
if board[row][i] == str(val):
return False
# 判断同一列是否冲突
for j in range(9):
if board[j][col] == str(val):
return False
# 判断同一九宫格是否有冲突
start_row = (row // 3) * 3
start_col = (col // 3) * 3
for i in range(start_row, start_row + 3):
for j in range(start_col, start_col + 3):
if board[i][j] == str(val):
return False
for j in range(9): #判断列里是否重复
if board[j][col] == str(val):
return False
startRow = (row // 3) * 3
startcol = (col // 3) * 3
for i in range(startRow,startRow + 3): #判断9方格里是否重复
for j in range(startcol,startcol + 3):
if board[i][j] == str(val):
return False
return True
backtrack(board)
```
Python3:
```python3
class Solution:
def __init__(self) -> None:
self.board = []
def isValid(self, row: int, col: int, target: int) -> bool:
for idx in range(len(self.board)):
# 同列是否重复
if self.board[idx][col] == str(target):
return False
# 同行是否重复
if self.board[row][idx] == str(target):
return False
# 9宫格里是否重复
box_row, box_col = (row // 3) * 3 + idx // 3, (col // 3) * 3 + idx % 3
if self.board[box_row][box_col] == str(target):
return False
return True
def getPlace(self) -> List[int]:
for row in range(len(self.board)):
for col in range(len(self.board)):
if self.board[row][col] == ".":
return [row, col]
return [-1, -1]
def isSolved(self) -> bool:
row, col = self.getPlace() # 找个空位置
if row == -1 and col == -1: # 没有空位置,棋盘被填满的
return True
for i in range(1, 10):
if self.isValid(row, col, i): # 检查这个空位置放i是否合适
self.board[row][col] = str(i) # 放i
if self.isSolved(): # 合适,立刻返回, 填下一个空位置。
return True
self.board[row][col] = "." # 不合适,回溯
return False # 空位置没法解决
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
if board is None or len(board) == 0:
return
self.board = board
self.isSolved()
```
Go
```go
func solveSudoku(board [][]byte) {
var backtracking func(board [][]byte) bool
backtracking=func(board [][]byte) bool{
for i:=0;i<9;i++{
for j:=0;j<9;j++{
//判断此位置是否适合填数字
if board[i][j]!='.'{
continue
}
//尝试填1-9
for k:='1';k<='9';k++{
if isvalid(i,j,byte(k),board)==true{//如果满足要求就填
board[i][j]=byte(k)
if backtracking(board)==true{
return true
}
board[i][j]='.'
}
}
return false
}
}
return true
}
backtracking(board)
}
//判断填入数字是否满足要求
func isvalid(row,col int,k byte,board [][]byte)bool{
for i:=0;i<9;i++{//行
if board[row][i]==k{
return false
}
}
for i:=0;i<9;i++{//列
if board[i][col]==k{
return false
}
}
//方格
startrow:=(row/3)*3
startcol:=(col/3)*3
for i:=startrow;i<startrow+3;i++{
for j:=startcol;j<startcol+3;j++{
if board[i][j]==k{
return false
}
}
}
return true
}
```
Javascript:
```Javascript
var solveSudoku = function(board) {
@ -432,9 +442,73 @@ var solveSudoku = function(board) {
};
```
C:
```C
bool isValid(char** board, int row, int col, int k) {
/* 判断当前行是否有重复元素 */
for (int i = 0; i < 9; i++) {
if (board[i][col] == k) {
return false;
}
}
/* 判断当前列是否有重复元素 */
for (int j = 0; j < 9; j++) {
if (board[row][j] == k) {
return false;
}
}
/* 计算当前9宫格左上角的位置 */
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
/* 判断当前元素所在九宫格是否有重复元素 */
for (int i = startRow; i < startRow + 3; i++) {
for (int j = startCol; j < startCol + 3; j++) {
if (board[i][j] == k) {
return false;
}
}
}
/* 满足条件返回true */
return true;
}
bool backtracking(char** board, int boardSize, int* boardColSize) {
/* 从上到下、从左到右依次遍历输入数组 */
for (int i = 0; i < boardSize; i++) {
for (int j = 0; j < *boardColSize; j++) {
/* 遇到数字跳过 */
if (board[i][j] != '.') {
continue;
}
/* 依次将数组1到9填入当前位置 */
for (int k = '1'; k <= '9'; k++) {
/* 判断当前位置是否与满足条件,是则进入下一层 */
if (isValid(board, i, j, k)) {
board[i][j] = k;
/* 判断下一层递归之后是否找到一种解法是则返回true */
if (backtracking(board, boardSize, boardColSize)) {
return true;
}
/* 回溯,将当前位置清零 */
board[i][j] = '.';
}
}
/* 若填入的9个数均不满足条件返回false说明此解法无效 */
return false;
}
}
/* 遍历完所有的棋盘没有返回false说明找到了解法返回true */
return true;
}
void solveSudoku(char** board, int boardSize, int* boardColSize) {
bool res = backtracking(board, boardSize, boardColSize);
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -7,7 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 39. 组合总和
# 39. 组合总和
[力扣题目链接](https://leetcode-cn.com/problems/combination-sum/)
@ -37,21 +37,21 @@ candidates 中的数字可以无限制重复被选取。
  [3,5]
]
## 思路
# 思路
[B站视频讲解-组合总和](https://www.bilibili.com/video/BV1KT4y1M7HJ)
题目中的**无限制重复被选取,吓得我赶紧想想 出现0 可咋办**然后看到下面提示1 <= candidates[i] <= 200我就放心了。
本题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)[回溯算法:求组合总和](https://programmercarl.com/0216.组合总和III.html)和区别是:本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。
本题和[77.组合](https://programmercarl.com/0077.组合.html)[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)和区别是:本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。
本题搜索的过程抽象成树形结构如下:
![39.组合总和](https://img-blog.csdnimg.cn/20201223170730367.png)
注意图中叶子节点的返回条件因为本题没有组合数量要求仅仅是总和的限制所以递归没有层数的限制只要选取的元素总和超过target就返回
而在[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:求组合总和](https://programmercarl.com/0216.组合总和III.html) 中都可以知道要递归K层因为要取k个元素的组合。
而在[77.组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html) 中都可以知道要递归K层因为要取k个元素的组合。
## 回溯三部曲
@ -65,9 +65,9 @@ candidates 中的数字可以无限制重复被选取。
**本题还需要startIndex来控制for循环的起始位置对于组合问题什么时候需要startIndex呢**
我举过例子如果是一个集合来求组合的话就需要startIndex例如[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)[回溯算法:求组合总和](https://programmercarl.com/0216.组合总和III.html)。
我举过例子如果是一个集合来求组合的话就需要startIndex例如[77.组合](https://programmercarl.com/0077.组合.html)[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)。
如果是多个集合取组合各个集合之间相互不影响那么就不用startIndex例如[回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
如果是多个集合取组合各个集合之间相互不影响那么就不用startIndex例如[17.电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我再讲解排列的时候就重点介绍**
@ -103,7 +103,7 @@ if (sum == target) {
单层for循环依然是从startIndex开始搜索candidates集合。
**注意本题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)、[回溯算法:求组合总和](https://programmercarl.com/0216.组合总和III.html)的一个区别是:本题元素为可重复选取的**
**注意本题和[77.组合](https://programmercarl.com/0077.组合.html)、[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)的一个区别是:本题元素为可重复选取的**
如何重复选取呢,看代码,注释部分:
@ -211,16 +211,16 @@ public:
};
```
## 总结
# 总结
本题和我们之前讲过的[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)、[回溯算法:求组合总和](https://programmercarl.com/0216.组合总和III.html)有两点不同:
本题和我们之前讲过的[77.组合](https://programmercarl.com/0077.组合.html)、[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)有两点不同:
* 组合没有数量要求
* 元素可无限重复选取
针对这两个问题,我都做了详细的分析。
并且给出了对于组合问题什么时候用startIndex什么时候不用并用[回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)做了对比。
并且给出了对于组合问题什么时候用startIndex什么时候不用并用[17.电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)做了对比。
最后还给出了本题的剪枝优化,这个优化如果是初学者的话并不容易想到。
@ -232,10 +232,10 @@ public:
## 其他语言版本
# 其他语言版本
Java
## Java
```Java
// 剪枝优化
class Solution {
@ -264,30 +264,77 @@ class Solution {
}
```
Python
## Python
**回溯**
```python3
class Solution:
def __init__(self):
self.path = []
self.paths = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
path = []
def backtrack(candidates,target,sum,startIndex):
if sum > target: return
if sum == target: return res.append(path[:])
for i in range(startIndex,len(candidates)):
if sum + candidates[i] >target: return #如果 sum + candidates[i] > target 就终止遍历
sum += candidates[i]
path.append(candidates[i])
backtrack(candidates,target,sum,i) #startIndex = i:表示可以重复读取当前的数
sum -= candidates[i] #回溯
path.pop() #回溯
candidates = sorted(candidates) #需要排序
backtrack(candidates,target,0,0)
return res
'''
因为本题没有组合数量限制所以只要元素总和大于target就算结束
'''
self.path.clear()
self.paths.clear()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:]) # 因为是shallow copy所以不能直接传入self.path
return
if sum_ > target:
return
# 单层递归逻辑
for i in range(start_index, len(candidates)):
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取所以不是i-1
sum_ -= candidates[i] # 回溯
self.path.pop() # 回溯
```
Go
**剪枝回溯**
```python3
class Solution:
def __init__(self):
self.path = []
self.paths = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
'''
因为本题没有组合数量限制所以只要元素总和大于target就算结束
'''
self.path.clear()
self.paths.clear()
> 主要在于递归中传递下一个数字
# 为了剪枝需要提前进行排序
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:]) # 因为是shallow copy所以不能直接传入self.path
return
# 单层递归逻辑
# 如果本层 sum + condidates[i] > target就提前结束遍历剪枝
for i in range(start_index, len(candidates)):
if sum_ + candidates[i] > target:
return
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取所以不是i-1
sum_ -= candidates[i] # 回溯
self.path.pop() # 回溯
```
## Go
主要在于递归中传递下一个数字
```go
func combinationSum(candidates []int, target int) [][]int {
@ -319,7 +366,8 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
}
```
JavaScript
## JavaScript
```js
var combinationSum = function(candidates, target) {
@ -346,7 +394,7 @@ var combinationSum = function(candidates, target) {
};
```
C:
## C
```c
int* path;
int pathTop;
@ -405,4 +453,4 @@ int** combinationSum(int* candidates, int candidatesSize, int target, int* retur
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -9,7 +9,7 @@
> 这篇可以说是全网把组合问题如何去重,讲的最清晰的了!
## 40.组合总和II
# 40.组合总和II
[力扣题目链接](https://leetcode-cn.com/problems/combination-sum-ii/)
@ -39,7 +39,7 @@ candidates 中的每个数字在每个组合中只能使用一次。
  [5]
]
## 思路
# 思路
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
@ -157,7 +157,6 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
**注意sum + candidates[i] <= target为剪枝操作在[39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)有讲解过!**
## C++代码
回溯三部曲分析完了整体C++代码如下:
@ -242,7 +241,7 @@ public:
```
## 总结
# 总结
本题同样是求组合总和但就是因为其数组candidates有重复元素而要求不能有重复的组合所以相对于[39.组合总和](https://programmercarl.com/0039.组合总和.html)难度提升了不少。
@ -254,10 +253,10 @@ public:
## 其他语言版本
# 其他语言版本
Java
## Java
```Java
class Solution {
List<List<Integer>> lists = new ArrayList<>();
@ -295,30 +294,97 @@ class Solution {
}
}
```
Python
```python
## Python
**回溯+巧妙去重(省去使用used**
```python3
class Solution:
def __init__(self):
self.paths = []
self.path = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
path = []
def backtrack(candidates,target,sum,startIndex):
if sum == target: res.append(path[:])
for i in range(startIndex,len(candidates)): #要对同一树层使用过的元素进行跳过
if sum + candidates[i] > target: return
if i > startIndex and candidates[i] == candidates[i-1]: continue #直接用startIndex来去重,要对同一树层使用过的元素进行跳过
sum += candidates[i]
path.append(candidates[i])
backtrack(candidates,target,sum,i+1) #i+1:每个数字在每个组合中只能使用一次
sum -= candidates[i] #回溯
path.pop() #回溯
candidates = sorted(candidates) #首先把给candidates排序让其相同的元素都挨在一起。
backtrack(candidates,target,0,0)
return res
'''
类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
'''
self.paths.clear()
self.path.clear()
# 必须提前进行数组排序,避免重复
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(candidates)):
# 剪枝同39.组合总和
if sum_ + candidates[i] > target:
return
# 跳过同一树层使用过的元素
if i > start_index and candidates[i] == candidates[i-1]:
continue
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i+1)
self.path.pop() # 回溯为了下一轮for loop
sum_ -= candidates[i] # 回溯为了下一轮for loop
```
Go
**回溯+去重使用used**
```python3
class Solution:
def __init__(self):
self.paths = []
self.path = []
self.used = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
'''
类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
本题需要使用used用来标记区别同一树层的元素使用重复情况注意区分递归纵向遍历遇到的重复元素和for循环遇到的重复元素这两者的区别
'''
self.paths.clear()
self.path.clear()
self.usage_list = [False] * len(candidates)
# 必须提前进行数组排序,避免重复
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
> 主要在于如何在回溯中去重
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(candidates)):
# 剪枝同39.组合总和
if sum_ + candidates[i] > target:
return
# 检查同一树层是否出现曾经使用过的相同元素
# 若数组中前后元素值相同,但前者却未被使用(used == False)说明是for loop中的同一树层的相同元素情况
if i > 0 and candidates[i] == candidates[i-1] and self.usage_list[i-1] == False:
continue
sum_ += candidates[i]
self.path.append(candidates[i])
self.usage_list[i] = True
self.backtracking(candidates, target, sum_, i+1)
self.usage_list[i] = False # 回溯为了下一轮for loop
self.path.pop() # 回溯为了下一轮for loop
sum_ -= candidates[i] # 回溯为了下一轮for loop
```
## Go
主要在于如何在回溯中去重
```go
func combinationSum2(candidates []int, target int) [][]int {
@ -359,7 +425,8 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int,
}
}
```
javaScript
## javaScript
```js
/**
@ -392,7 +459,8 @@ var combinationSum2 = function(candidates, target) {
}
};
```
C:
## C
```c
int* path;
int pathTop;
@ -457,4 +525,4 @@ int** combinationSum2(int* candidates, int candidatesSize, int target, int* retu
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
> 这个图就是大厂面试经典题目,接雨水! 最常青藤的一道题,面试官百出不厌!
# 42. 接雨水
@ -134,13 +134,16 @@ public:
因为每次遍历列的时候还要向两边寻找最高的列所以时间复杂度为O(n^2)。
空间复杂度为O(1)。
## 动态规划解法
在上一节的双指针解法中,我们可以看到只要记录左边柱子的最高高度 和 右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。
当前列雨水面积min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。
为了到两边的最高高度使用了双指针来遍历每到一个柱子都向两边遍历一遍这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上maxLeft右边最高高度记录在一个数组上maxRight。这样就避免了重复计算这就用到了动态规划。
为了到两边的最高高度使用了双指针来遍历每到一个柱子都向两边遍历一遍这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上maxLeft右边最高高度记录在一个数组上maxRight。这样就避免了重复计算这就用到了动态规划。
当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。
@ -201,7 +204,7 @@ public:
2. 使用单调栈内元素的顺序
从大到小还是从小到呢?
从大到小还是从小到呢?
从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
@ -391,6 +394,7 @@ class Solution {
}
}
```
动态规划法
```java
class Solution {
@ -418,6 +422,54 @@ class Solution {
}
}
```
单调栈法
```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<Integer> stack = new Stack<Integer>();
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:
双指针法
@ -463,29 +515,236 @@ class Solution:
```python3
class Solution:
def trap(self, height: List[int]) -> int:
st =[0]
# 单调栈
'''
单调栈是按照 行 的方向来计算雨水
从栈顶到栈底的顺序:从小到大
通过三个元素来接水:栈顶,栈顶的下一个元素,以及即将入栈的元素
雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度
雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1因为只求中间宽度
'''
# stack储存index用于计算对应的柱子高度
stack = [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)
for i in range(1, len(height)):
# 情况一
if height[i] < height[stack[-1]]:
stack.append(i)
# 情况二
# 当当前柱子高度和栈顶一致时,左边的一个是不可能存放雨水的,所以保留右侧新柱子
# 需要使用最右边的柱子来计算宽度
elif height[i] == height[stack[-1]]:
stack.pop()
stack.append(i)
# 情况三
else:
# 抛出所有较低的柱子
while stack and height[i] > height[stack[-1]]:
# 栈顶就是中间的柱子:储水槽,就是凹槽的地步
mid_height = height[stack[-1]]
stack.pop()
if stack:
right_height = height[i]
left_height = height[stack[-1]]
# 两侧的较矮一方的高度 - 凹槽底部高度
h = min(right_height, left_height) - mid_height
# 凹槽右侧下标 - 凹槽左侧下标 - 1: 只求中间宽度
w = i - stack[-1] - 1
# 体积:高乘宽
result += h * w
stack.append(i)
return result
# 单调栈压缩版
class Solution:
def trap(self, height: List[int]) -> int:
stack = [0]
result = 0
for i in range(1, len(height)):
while stack and height[i] > height[stack[-1]]:
mid_height = stack.pop()
if stack:
# 雨水高度是 min(凹槽左侧高度, 凹槽右侧高度) - 凹槽底部高度
h = min(height[stack[-1]], height[i]) - height[mid_height]
# 雨水宽度是 凹槽右侧的下标 - 凹槽左侧的下标 - 1
w = i - stack[-1] - 1
# 累计总雨水体积
result += h * w
stack.append(i)
return result
```
Go:
```go
func trap(height []int) int {
var left, right, leftMax, rightMax, res int
right = len(height) - 1
for left < right {
if height[left] < height[right] {
if height[left] >= leftMax {
leftMax = height[left] // 设置左边最高柱子
} else {
res += leftMax - height[left] // //右边必定有柱子挡水所以遇到所有值小于等于leftMax的全部加入水池中
}
left++
} else {
if height[right] > rightMax {
rightMax = height[right] // //设置右边最高柱子
} else {
res += rightMax - height[right] // //左边必定有柱子挡水所以遇到所有值小于等于rightMax的全部加入水池
}
right--
}
}
return res
}
```
JavaScript:
```javascript
//双指针
var trap = function(height) {
const len = height.length;
let sum = 0;
for(let i = 0; i < len; i++){
// 第一个柱子和最后一个柱子不接雨水
if(i == 0 || i == len - 1) continue;
let rHeight = height[i]; // 记录右边柱子的最高高度
let lHeight = height[i]; // 记录左边柱子的最高高度
for(let r = i + 1; r < len; r++){
if(height[r] > rHeight) rHeight = height[r];
}
for(let l = i - 1; l >= 0; l--){
if(height[l] > lHeight) lHeight = height[l];
}
let h = Math.min(lHeight, rHeight) - height[i];
if(h > 0) sum += h;
}
return sum;
};
//动态规划
var trap = function(height) {
const len = height.length;
if(len <= 2) return 0;
const maxLeft = new Array(len).fill(0);
const maxRight = new Array(len).fill(0);
// 记录每个柱子左边柱子最大高度
maxLeft[0] = height[0];
for(let i = 1; i < len; i++){
maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
}
// 记录每个柱子右边柱子最大高度
maxRight[len - 1] = height[len - 1];
for(let i = len - 2; i >= 0; i--){
maxRight[i] = Math.max(height[i], maxRight[i + 1]);
}
// 求和
let sum = 0;
for(let i = 0; i < len; i++){
let count = Math.min(maxLeft[i], maxRight[i]) - height[i];
if(count > 0) sum += count;
}
return sum;
};
//单调栈 js数组作为栈
var trap = function(height) {
const len = height.length;
if(len <= 2) return 0; // 可以不加
const st = [];// 存着下标,计算的时候用下标对应的柱子高度
st.push(0);
let sum = 0;
for(let i = 1; i < len; i++){
if(height[i] < height[st[st.length - 1]]){ // 情况一
st.push(i);
}
if (height[i] == height[st[st.length - 1]]) { // 情况二
st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。
st.push(i);
} else { // 情况三
while (st.length !== 0 && height[i] > height[st[st.length - 1]]) { // 注意这里是while
let mid = st[st.length - 1];
st.pop();
if (st.length !== 0) {
let h = Math.min(height[st[st.length - 1]], height[i]) - height[mid];
let w = i - st[st.length - 1] - 1; // 注意减一,只求中间宽度
sum += h * w;
}
}
st.push(i);
}
}
return sum;
};
//单调栈 简洁版本 只处理情况三
var trap = function(height) {
const len = height.length;
if(len <= 2) return 0; // 可以不加
const st = [];// 存着下标,计算的时候用下标对应的柱子高度
st.push(0);
let sum = 0;
for(let i = 1; i < len; i++){ // 只处理的情况三,其实是把情况一和情况二融合了
while (st.length !== 0 && height[i] > height[st[st.length - 1]]) { // 注意这里是while
let mid = st[st.length - 1];
st.pop();
if (st.length !== 0) {
let h = Math.min(height[st[st.length - 1]], height[i]) - height[mid];
let w = i - st[st.length - 1] - 1; // 注意减一,只求中间宽度
sum += h * w;
}
}
st.push(i);
}
return sum;
};
```
C:
一种更简便的双指针方法:
之前的双指针方法的原理是固定“底”的位置,往两边找比它高的“壁”,循环若干次求和。
我们逆向思维,把“壁”用两个初始位置在数组首末位置的指针表示,“壁”往中间推,同样可以让每个“底”都能找到最高的“壁”
本质上就是改变了运算方向,从而减少了重复运算
代码如下:
```C
int trap(int* height, int heightSize) {
int ans = 0;
int left = 0, right = heightSize - 1; //初始化两个指针到左右两边
int leftMax = 0, rightMax = 0; //这两个值用来记录左右的“壁”的最高值
while (left < right) { //两个指针重合就结束
leftMax = fmax(leftMax, height[left]);
rightMax = fmax(rightMax, height[right]);
if (leftMax < rightMax) {
ans += leftMax - height[left]; //这里考虑的是下标为left的“底”能装多少水
++left;//指针的移动次序是这个方法的关键
//这里左指针右移是因为左“墙”较矮,左边这一片实际情况下的盛水量是受制于这个矮的左“墙”的
//而较高的右边在实际情况下的限制条件可能不是当前的左“墙”,比如限制条件可能是右“墙”,就能装更高的水,
}
else {
ans += rightMax - height[right]; //同理考虑下标为right的元素
--right;
}
}
return ans;
}
```
时间复杂度 O(n)
空间复杂度 O(1)
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -211,44 +211,68 @@ class Solution {
```
Python
**回溯**
```python3
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
res = [] #存放符合条件结果的集合
path = [] #用来存放符合条件的结果
used = [] #用来存放已经用过的数字
def backtrack(nums,used):
if len(path) == len(nums):
return res.append(path[:]) #此时说明找到了一组
for i in range(0,len(nums)):
if nums[i] in used:
continue #used里已经收录的元素直接跳过
path.append(nums[i])
used.append(nums[i])
backtrack(nums,used)
used.pop()
path.pop()
backtrack(nums,used)
return res
```
def __init__(self):
self.path = []
self.paths = []
Python优化不用used数组
def permute(self, nums: List[int]) -> List[List[int]]:
'''
因为本题排列是有序的,这意味着同一层的元素可以重复使用,但同一树枝上不能重复使用(usage_list)
所以处理排列问题每层都需要从头搜索故不再使用start_index
'''
usage_list = [False] * len(nums)
self.backtracking(nums, usage_list)
return self.paths
def backtracking(self, nums: List[int], usage_list: List[bool]) -> None:
# Base Case本题求叶子节点
if len(self.path) == len(nums):
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(0, len(nums)): # 从头开始搜索
# 若遇到self.path里已收录的元素跳过
if usage_list[i] == True:
continue
usage_list[i] = True
self.path.append(nums[i])
self.backtracking(nums, usage_list) # 纵向传递使用信息,去重
self.path.pop()
usage_list[i] = False
```
**回溯+丢掉usage_list**
```python3
class Solution:
def __init__(self):
self.path = []
self.paths = []
def permute(self, nums: List[int]) -> List[List[int]]:
res = [] #存放符合条件结果的集合
path = [] #用来存放符合条件的结果
def backtrack(nums):
if len(path) == len(nums):
return res.append(path[:]) #此时说明找到了一组
for i in range(0,len(nums)):
if nums[i] in path: #path里已经收录的元素直接跳过
continue
path.append(nums[i])
backtrack(nums) #递归
path.pop() #回溯
backtrack(nums)
return res
'''
因为本题排列是有序的,这意味着同一层的元素可以重复使用,但同一树枝上不能重复使用
所以处理排列问题每层都需要从头搜索故不再使用start_index
'''
self.backtracking(nums)
return self.paths
def backtracking(self, nums: List[int]) -> None:
# Base Case本题求叶子节点
if len(self.path) == len(nums):
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(0, len(nums)): # 从头开始搜索
# 若遇到self.path里已收录的元素跳过
if nums[i] in self.path:
continue
self.path.append(nums[i])
self.backtracking(nums)
self.path.pop()
```
Go
@ -381,4 +405,4 @@ int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -33,11 +33,11 @@
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
这道题目和[回溯算法:排列问题!](https://programmercarl.com/0046.全排列.html)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。
这道题目和[46.全排列](https://programmercarl.com/0046.全排列.html)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。
这里又涉及到去重了。
在[回溯算法:求组合总和(三)](https://programmercarl.com/0040.组合总和II.html) 、[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)我们分别详细讲解了组合问题和子集问题如何去重。
在[40.组合总和II](https://programmercarl.com/0040.组合总和II.html) 、[90.子集II](https://programmercarl.com/0090.子集II.html)我们分别详细讲解了组合问题和子集问题如何去重。
那么排列问题其实也是一样的套路。
@ -51,11 +51,11 @@
**一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果**
在[回溯算法:排列问题!](https://programmercarl.com/0046.全排列.html)中已经详解讲解了排列问题的写法,在[回溯算法:求组合总和(三)](https://programmercarl.com/0040.组合总和II.html) 、[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)中详细讲解的去重的写法,所以这次我就不用回溯三部曲分析了,直接给出代码,如下:
在[46.全排列](https://programmercarl.com/0046.全排列.html)中已经详解讲解了排列问题的写法,在[40.组合总和II](https://programmercarl.com/0040.组合总和II.html) 、[90.子集II](https://programmercarl.com/0090.子集II.html)中详细讲解的去重的写法,所以这次我就不用回溯三部曲分析了,直接给出代码,如下:
## C++代码
```
```CPP
class Solution {
private:
vector<vector<int>> result;
@ -338,4 +338,4 @@ func backTring(nums,subRes []int,res *[][]int,used []bool){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -147,7 +147,7 @@ for (int col = 0; col < n; col++) {
代码如下:
```
```CPP
bool isValid(int row, int col, vector<string>& chessboard, int n) {
int count = 0;
// 检查列
@ -499,4 +499,4 @@ var solveNQueens = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -8,6 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 52. N皇后II
题目链接https://leetcode-cn.com/problems/n-queens-ii/
@ -101,4 +101,52 @@ public:
```
# 其他语言补充
JavaScript
```javascript
var totalNQueens = function(n) {
let count = 0;
const backtracking = (n, row, chessboard) => {
if(row === n){
count++;
return;
}
for(let col = 0; col < n; col++){
if(isValid(row, col, chessboard, n)) { // 验证合法就可以放
chessboard[row][col] = 'Q'; // 放置皇后
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.'; // 回溯
}
}
}
const isValid = (row, col, chessboard, n) => {
// 检查列
for(let i = 0; i < row; i++){ // 这是一个剪枝
if(chessboard[i][col] === 'Q'){
return false;
}
}
// 检查 45度角是否有皇后
for(let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
if(chessboard[i][j] === 'Q'){
return false;
}
}
// 检查 135度角是否有皇后
for(let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++){
if(chessboard[i][j] === 'Q'){
return false;
}
}
return true;
}
const chessboard = new Array(n).fill([]).map(() => new Array(n).fill('.'));
backtracking(n, 0, chessboard);
return count;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -103,6 +103,8 @@ public:
当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。
不少同学认为 如果输入用例都是-1或者 都是负数这个贪心算法跑出来的结果是0 这是**又一次证明脑洞模拟不靠谱的经典案例**,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。
## 动态规划
当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列后续到动态规划系列的时候会详细讲解本题的dp方法。
@ -214,4 +216,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -173,7 +173,8 @@ JavaScript
```javascript
const maxSubArray = nums => {
// 数组长度dp初始化
const [len, dp] = [nums.length, [nums[0]]];
const len = nums.length;
let dp = new Array(len).fill(0);
// 最大值初始化为dp[0]
let max = dp[0];
for (let i = 1; i < len; i++) {
@ -191,4 +192,4 @@ const maxSubArray = nums => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -241,6 +241,32 @@ var merge = function (intervals) {
return result
};
```
版本二左右区间
```javascript
/**
* @param {number[][]} intervals
* @return {number[][]}
*/
var merge = function(intervals) {
let n = intervals.length;
if ( n < 2) return intervals;
intervals.sort((a, b) => a[0]- b[0]);
let res = [],
left = intervals[0][0],
right = intervals[0][1];
for (let i = 1; i < n; i++) {
if (intervals[i][0] > right) {
res.push([left, right]);
left = intervals[i][0];
right = intervals[i][1];
} else {
right = Math.max(intervals[i][1], right);
}
}
res.push([left, right]);
return res;
};
```
@ -248,4 +274,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -11,7 +11,8 @@
## 59.螺旋矩阵II
[力扣题目链接](https://leetcode-cn.com/problems/spiral-matrix-ii/)
给定一个正整数 n生成一个包含 1 到 n2 所有元素且元素按顺时针顺序螺旋排列的正方形矩阵。
给定一个正整数 n生成一个包含 1 到 n^2 所有元素且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
@ -238,45 +239,54 @@ class Solution:
javaScript
```js
```javascript
/**
* @param {number} n
* @return {number[][]}
*/
var generateMatrix = function(n) {
// new Array(n).fill(new Array(n))
// 使用fill --> 填充的是同一个数组地址
const res = Array.from({length: n}).map(() => new Array(n));
let loop = n >> 1, i = 0, //循环次
count = 1,
startX = startY = 0; // 起始位置
while(++i <= loop) {
// 定义行列
let row = startX, column = startY;
// [ startY, n - i)
while(column < n - i) {
res[row][column++] = count++;
let startX = startY = 0; // 起始位置
let loop = Math.floor(n/2); // 旋转圈数
let mid = Math.floor(n/2); // 中间位置
let offset = 1; // 控制每一层填充元素个
let count = 1; // 更新填充数字
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
while (loop--) {
let row = startX, col = startY;
// 上行从左到右(左闭右开)
for (; col < startY + n - offset; col++) {
res[row][col] = count++;
}
// [ startX, n - i)
while(row < n - i) {
res[row++][column] = count++;
// 右列从上到下(左闭右开)
for (; row < startX + n - offset; row++) {
res[row][col] = count++;
}
// [n - i , startY)
while(column > startY) {
res[row][column--] = count++;
// 下行从右到左(左闭右开)
for (; col > startX; col--) {
res[row][col] = count++;
}
// [n - i , startX)
while(row > startX) {
res[row--][column] = count++;
// 左列做下到上(左闭右开)
for (; row > startY; row--) {
res[row][col] = count++;
}
startX = ++startY;
// 更新起始位置
startX++;
startY++;
// 更新offset
offset += 2;
}
if(n & 1) {
res[startX][startY] = count;
// 如果n为奇数的话需要单独给矩阵最中间的位置赋值
if (n % 2 === 1) {
res[mid][mid] = count;
}
return res;
};
```
Go:
@ -532,4 +542,4 @@ int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -327,10 +327,29 @@ var uniquePaths = function(m, n) {
return dp[m - 1][n - 1]
};
```
>版本二直接将dp数值值初始化为1
```javascript
/**
* @param {number} m
* @param {number} n
* @return {number}
*/
var uniquePaths = function(m, n) {
let dp = new Array(m).fill(1).map(() => new Array(n).fill(1));
// dp[i][j] 表示到达ij 点的路径数
for (let i=1; i<m; i++) {
for (let j=1; j< n;j++) {
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -88,7 +88,7 @@
以上分析完毕C++代码如下:
```
```CPP
class Solution {
public:
int climbStairs(int n) {
@ -186,10 +186,24 @@ func climbStairs(n int) int {
}
```
JavaScript:
```javascript
var climbStairs = function(n) {
const dp = new Array(n+1).fill(0);
const weight = [1,2];
dp[0] = 1;
for(let i = 0; i <= n; i++){ //先遍历背包
for(let j = 0; j < weight.length; j++){ // 再遍历物品
if(i >= weight[j]) dp[i] += dp[i-weight[j]];
}
}
return dp[n];
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -51,7 +51,6 @@ exection -> execution (插入 'u')
接下来我依然使用动规五部曲,对本题做一个详细的分析:
-----------------------
### 1. 确定dp数组dp table以及下标的含义
@ -61,7 +60,6 @@ exection -> execution (插入 'u')
用i来表示也可以 但我统一以下标i-1为结尾的字符串在下面的递归公式中会容易理解一点。
-----------------------
### 2. 确定递推公式
@ -156,7 +154,6 @@ for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
```
-----------------------
### 4. 确定遍历顺序
@ -187,7 +184,6 @@ for (int i = 1; i <= word1.size(); i++) {
}
}
```
-----------------------
### 5. 举例推导dp数组
@ -220,7 +216,6 @@ public:
};
```
-----------------------
## 其他语言版本
@ -338,4 +333,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -29,7 +29,7 @@
也可以直接看我的B站视频[带你学透回溯算法-组合问题对应力扣题目77.组合)](https://www.bilibili.com/video/BV1ti4y1L7cv#reply3733925949)
## 思路
# 思路
本题这是回溯法的经典题目。
@ -37,7 +37,7 @@
直接的解法当然是使用for循环例如示例中k为2很容易想到 用两个for循环这样就可以输出 和示例中一样的结果。
代码如下:
```
```CPP
int n = 4;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
@ -49,7 +49,7 @@ for (int i = 1; i <= n; i++) {
输入n = 100, k = 3
那么就三层for循环代码如下
```
```CPP
int n = 100;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
@ -301,7 +301,7 @@ for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜
优化后整体代码如下:
```
```CPP
class Solution {
private:
vector<vector<int>> result;
@ -336,10 +336,10 @@ public:
## 其他语言版本
# 其他语言版本
Java
## Java
```java
class Solution {
List<List<Integer>> result = new ArrayList<>();
@ -368,8 +368,54 @@ class Solution {
}
```
Python2:
```python
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
result = []
path = []
def backtracking(n, k, startidx):
if len(path) == k:
result.append(path[:])
return
Python
# 剪枝, 最后k - len(path)个节点直接构造结果,无需递归
last_startidx = n - (k - len(path)) + 1
result.append(path + [idx for idx in range(last_startidx, n + 1)])
for x in range(startidx, last_startidx):
path.append(x)
backtracking(n, k, x + 1) # 递归
path.pop() # 回溯
backtracking(n, k, 1)
return result
```
## Python
```python
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
path = []
def backtrack(n, k, StartIndex):
if len(path) == k:
res.append(path[:])
return
for i in range(StartIndex, n-(k-len(path)) + 2):
path.append(i)
backtrack(n, k, i+1)
path.pop()
backtrack(n, k, 1)
return res
```
剪枝
```python3
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
@ -378,15 +424,19 @@ class Solution:
def backtrack(n,k,startIndex):
if len(path) == k:
res.append(path[:])
return
for i in range(startIndex,n+1):
return
for i in range(startIndex,n-(k-len(path))+2): #优化的地方
path.append(i) #处理节点
backtrack(n,k,i+1) #递归
path.pop() #回溯,撤销处理的节点
backtrack(n,k,1)
return res
backtrack(n,k,1)
return res
```
javascript
## javascript
剪枝:
```javascript
let result = []
let path = []
@ -406,8 +456,11 @@ const combineHelper = (n, k, startIndex) => {
path.pop()
}
}
```
Go
```
## Go
```Go
var res [][]int
func combine(n int, k int) [][]int {
@ -434,8 +487,35 @@ func backtrack(n,k,start int,track []int){
}
}
```
剪枝:
```Go
var res [][]int
func combine(n int, k int) [][]int {
res=[][]int{}
if n <= 0 || k <= 0 || k > n {
return res
}
backtrack(n, k, 1, []int{})
return res
}
func backtrack(n,k,start int,track []int){
if len(track)==k{
temp:=make([]int,k)
copy(temp,track)
res=append(res,temp)
}
if len(track)+n-start+1 < k {
return
}
for i:=start;i<=n;i++{
track=append(track,i)
backtrack(n,k,i+1,track)
track=track[:len(track)-1]
}
}
```
C:
## C
```c
int* path;
int pathTop;
@ -489,8 +569,62 @@ int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
}
```
剪枝:
```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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -22,7 +22,7 @@
大家先回忆一下[77. 组合]给出的回溯法的代码:
```
```c++
class Solution {
private:
vector<vector<int>> result; // 存放符合条件结果的集合
@ -54,7 +54,7 @@ public:
在遍历的过程中有如下代码:
```
```c++
for (int i = startIndex; i <= n; i++) {
path.push_back(i);
backtracking(n, k, i + 1);
@ -78,7 +78,7 @@ for (int i = startIndex; i <= n; i++) {
**如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了**。
注意代码中i就是for循环里选择的起始位置。
```
```c++
for (int i = startIndex; i <= n; i++) {
```
@ -100,13 +100,13 @@ for (int i = startIndex; i <= n; i++) {
所以优化之后的for循环是
```
```c++
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置
```
优化后整体代码如下:
```
```c++
class Solution {
private:
vector<vector<int>> result;
@ -300,4 +300,4 @@ int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -7,7 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 78题. 子集
# 78.子集
[力扣题目链接](https://leetcode-cn.com/problems/subsets/)
@ -29,9 +29,9 @@
  []
]
## 思路
# 思路
求子集问题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:分割问题!](https://programmercarl.com/0131.分割回文串.html)又不一样了。
求子集问题和[77.组合](https://programmercarl.com/0077.组合.html)和[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)又不一样了。
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,**那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!**
@ -153,19 +153,19 @@ public:
并不会因为每次递归的下一层就是从i+1开始的。
## 总结
# 总结
相信大家经过了
* 组合问题:
* [回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)
* [77.组合](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)
* [216.组合总和III](https://programmercarl.com/0216.组合总和III.html)
* [17.电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
* [39.组合总和](https://programmercarl.com/0039.组合总和.html)
* [40.组合总和II](https://programmercarl.com/0040.组合总和II.html)
* 分割问题:
* [回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)
* [回溯算法:复原IP地址](https://programmercarl.com/0093.复原IP地址.html)
* [131.分割回文串](https://programmercarl.com/0131.分割回文串.html)
* [93.复原IP地址](https://programmercarl.com/0093.复原IP地址.html)
洗礼之后,发现子集问题还真的有点简单了,其实这就是一道标准的模板题。
@ -173,10 +173,10 @@ public:
**而组合问题、分割问题是收集树形结构中叶子节点的结果**
## 其他语言版本
# 其他语言版本
Java
## Java
```java
class Solution {
List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
@ -204,23 +204,34 @@ class Solution {
}
```
Python
## Python
```python3
class Solution:
def __init__(self):
self.path: List[int] = []
self.paths: List[List[int]] = []
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
path = []
def backtrack(nums,startIndex):
res.append(path[:]) #收集子集,要放在终止添加的上面,否则会漏掉自己
for i in range(startIndex,len(nums)): #当startIndex已经大于数组的长度了就终止了for循环本来也结束了所以不需要终止条件
path.append(nums[i])
backtrack(nums,i+1) #递归
path.pop() #回溯
backtrack(nums,0)
return res
self.paths.clear()
self.path.clear()
self.backtracking(nums, 0)
return self.paths
def backtracking(self, nums: List[int], start_index: int) -> None:
# 收集子集,要先于终止判断
self.paths.append(self.path[:])
# Base Case
if start_index == len(nums):
return
# 单层递归逻辑
for i in range(start_index, len(nums)):
self.path.append(nums[i])
self.backtracking(nums, i+1)
self.path.pop() # 回溯
```
Go
## Go
```Go
var res [][]int
func subset(nums []int) [][]int {
@ -244,7 +255,7 @@ func Dfs(temp, nums []int, start int){
}
```
Javascript:
## Javascript:
```Javascript
var subsets = function(nums) {
@ -263,7 +274,7 @@ var subsets = function(nums) {
};
```
C:
## C
```c
int* path;
int pathTop;
@ -325,4 +336,4 @@ int** subsets(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,3 +1,10 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 84.柱状图中最大的矩形
@ -228,53 +235,261 @@ class Solution {
}
```
单调栈
```java
class Solution {
int largestRectangleArea(int[] heights) {
Stack<Integer> st = new Stack<Integer>();
// 数组扩容,在头和尾各加入一个元素
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<len(heights) and heights[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]<heights[st[-1]]:
midh = heights[st[-1]]
st.pop()
if st!=[]:
minrightindex = i
minleftindex = st[-1]
summ = (minrightindex-minleftindex-1)*midh
result = max(summ,result)
st.append(i)
return result
```
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
# 双指针暴力解法leetcode超时
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# 从左向右遍历:以每一根柱子为主心骨(当前轮最高的参照物),迭代直到找到左侧和右侧各第一个矮一级的柱子
res = 0
for i in range(len(heights)):
left = i
right = i
# 向左侧遍历:寻找第一个矮一级的柱子
for _ in range(left, -1, -1):
if heights[left] < heights[i]:
break
left -= 1
# 向右侧遍历:寻找第一个矮一级的柱子
for _ in range(right, len(heights)):
if heights[right] < heights[i]:
break
right += 1
width = right - left - 1
height = heights[i]
res = max(res, width * height)
return res
# DP动态规划
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
size = len(heights)
# 两个DP数列储存的均是下标index
min_left_index = [0] * size
min_right_index = [0] * size
result = 0
# 记录每个柱子的左侧第一个矮一级的柱子的下标
min_left_index[0] = -1 # 初始化防止while死循环
for i in range(1, size):
# 以当前柱子为主心骨,向左迭代寻找次级柱子
temp = i - 1
while temp >= 0 and heights[temp] >= heights[i]:
# 当左侧的柱子持续较高时尝试这个高柱子自己的次级柱子DP
temp = min_left_index[temp]
# 当找到左侧矮一级的目标柱子时
min_left_index[i] = temp
# 记录每个柱子的右侧第一个矮一级的柱子的下标
min_right_index[size-1] = size # 初始化防止while死循环
for i in range(size-2, -1, -1):
# 以当前柱子为主心骨,向右迭代寻找次级柱子
temp = i + 1
while temp < size and heights[temp] >= heights[i]:
# 当右侧的柱子持续较高时尝试这个高柱子自己的次级柱子DP
temp = min_right_index[temp]
# 当找到右侧矮一级的目标柱子时
min_right_index[i] = temp
for i in range(size):
area = heights[i] * (min_right_index[i] - min_left_index[i] - 1)
result = max(area, result)
return result
# 单调栈
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# Monotonic Stack
'''
找每个柱子左右侧的第一个高度值小于该柱子的柱子
单调栈:栈顶到栈底:从大到小(每插入一个新的小数值时,都要弹出先前的大数值)
栈顶,栈顶的下一个元素,即将入栈的元素:这三个元素组成了最大面积的高度和宽度
情况一当前遍历的元素heights[i]大于栈顶元素的情况
情况二当前遍历的元素heights[i]等于栈顶元素的情况
情况三当前遍历的元素heights[i]小于栈顶元素的情况
'''
# 输入数组首尾各补上一个0与42.接雨水不同的是,本题原首尾的两个柱子可以作为核心柱进行最大面积尝试
heights.insert(0, 0)
heights.append(0)
stack = [0]
result = 0
for i in range(1, len(heights)):
# 情况一
if heights[i] > heights[stack[-1]]:
stack.append(i)
# 情况二
elif heights[i] == heights[stack[-1]]:
stack.pop()
stack.append(i)
# 情况三
else:
# 抛出所有较高的柱子
while stack and heights[i] < heights[stack[-1]]:
# 栈顶就是中间的柱子,主心骨
mid_index = stack[-1]
stack.pop()
if stack:
left_index = stack[-1]
right_index = i
width = right_index - left_index - 1
height = heights[mid_index]
result = max(result, width * height)
stack.append(i)
return result
# 单调栈精简
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
heights.insert(0, 0)
heights.append(0)
stack = [0]
result = 0
for i in range(1, len(heights)):
while stack and heights[i] < heights[stack[-1]]:
mid_height = heights[stack[-1]]
stack.pop()
if stack:
# area = width * height
area = (i - stack[-1] - 1) * mid_height
result = max(area, result)
stack.append(i)
return result
```
*****
JavaScript:
```javascript
//动态规划 js中运行速度最快
var largestRectangleArea = function(heights) {
const len = heights.length;
const minLeftIndex = new Array(len);
const maxRigthIndex = new Array(len);
// 记录每个柱子 左边第一个小于该柱子的下标
minLeftIndex[0] = -1; // 注意这里初始化防止下面while死循环
for(let i = 1; i < len; i++) {
let t = i - 1;
// 这里不是用if而是不断向左寻找的过程
while(t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
minLeftIndex[i] = t;
}
// 记录每个柱子 右边第一个小于该柱子的下标
maxRigthIndex[len - 1] = len; // 注意这里初始化防止下面while死循环
for(let i = len - 2; i >= 0; i--){
let t = i + 1;
// 这里不是用if而是不断向右寻找的过程
while(t < len && heights[t] >= heights[i]) t = maxRigthIndex[t];
maxRigthIndex[i] = t;
}
// 求和
let maxArea = 0;
for(let i = 0; i < len; i++){
let sum = heights[i] * (maxRigthIndex[i] - minLeftIndex[i] - 1);
maxArea = Math.max(maxArea , sum);
}
return maxArea;
};
//单调栈
var largestRectangleArea = function(heights) {
let maxArea = 0;
const stack = [];
heights = [0,...heights,0]; // 数组头部加入元素0 数组尾部加入元素0
for(let i = 0; i < heights.length; i++){
if(heights[i] > heights[stack[stack.length-1]]){ // 情况三
stack.push(i);
} else if(heights[i] === heights[stack[stack.length-1]]){ // 情况二
stack.pop(); // 这个可以加,可以不加,效果一样,思路不同
stack.push(i);
} else { // 情况一
while(heights[i] < heights[stack[stack.length-1]]){// 当前bar比栈顶bar矮
const stackTopIndex = stack.pop();// 栈顶元素出栈并保存栈顶bar的索引
let w = i - stack[stack.length -1] - 1;
let h = heights[stackTopIndex]
// 计算面积,并取最大面积
maxArea = Math.max(maxArea, w * h);
}
stack.push(i);// 当前bar比栈顶bar高了入栈
}
}
return maxArea;
};
//单调栈 简洁
var largestRectangleArea = function(heights) {
let maxArea = 0;
const stack = [];
heights = [0,...heights,0]; // 数组头部加入元素0 数组尾部加入元素0
for(let i = 0; i < heights.length; i++){ // 只用考虑情况一 当前遍历的元素heights[i]小于栈顶元素heights[stack[stack.length-1]]]的情况
while(heights[i] < heights[stack[stack.length-1]]){// 当前bar比栈顶bar矮
const stackTopIndex = stack.pop();// 栈顶元素出栈并保存栈顶bar的索引
let w = i - stack[stack.length -1] - 1;
let h = heights[stackTopIndex]
// 计算面积,并取最大面积
maxArea = Math.max(maxArea, w * h);
}
stack.push(i);// 当前bar比栈顶bar高了入栈
}
return maxArea;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -32,7 +32,7 @@
做本题之前一定要先做[78.子集](https://programmercarl.com/0078.子集.html)。
这道题目和[回溯算法:求子集问题!](https://programmercarl.com/0078.子集.html)区别就是集合里有重复元素了,而且求取的子集要去重。
这道题目和[78.子集](https://programmercarl.com/0078.子集.html)区别就是集合里有重复元素了,而且求取的子集要去重。
那么关于回溯算法中的去重问题,**在[40.组合总和II](https://programmercarl.com/0040.组合总和II.html)中已经详细讲解过了,和本题是一个套路**。
@ -48,7 +48,7 @@
## C++代码
```c++
```CPP
class Solution {
private:
vector<vector<int>> result;
@ -83,7 +83,7 @@ public:
```
使用set去重的版本。
```c++
```CPP
class Solution {
private:
vector<vector<int>> result;
@ -207,20 +207,30 @@ class Solution {
Python
```python3
class Solution:
def __init__(self):
self.paths = []
self.path = []
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
res = [] #存放符合条件结果的集合
path = [] #用来存放符合条件结果
def backtrack(nums,startIndex):
res.append(path[:])
for i in range(startIndex,len(nums)):
if i > startIndex and nums[i] == nums[i - 1]: #我们要对同一树层使用过的元素进行跳过
continue
path.append(nums[i])
backtrack(nums,i+1) #递归
path.pop() #回溯
nums = sorted(nums) #去重需要排序
backtrack(nums,0)
return res
nums.sort()
self.backtracking(nums, 0)
return self.paths
def backtracking(self, nums: List[int], start_index: int) -> None:
# ps.空集合仍符合要求
self.paths.append(self.path[:])
# Base Case
if start_index == len(nums):
return
# 单层递归逻辑
for i in range(start_index, len(nums)):
if i > start_index and nums[i] == nums[i-1]:
# 当前后元素值相同时,跳入下一个循环,去重
continue
self.path.append(nums[i])
self.backtracking(nums, i+1)
self.path.pop()
```
Go
@ -353,4 +363,4 @@ int** subsetsWithDup(int* nums, int numsSize, int* returnSize, int** returnColum
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -8,7 +8,7 @@
## 93.复原IP地址
# 93.复原IP地址
[力扣题目链接](https://leetcode-cn.com/problems/restore-ip-addresses/)
@ -19,37 +19,37 @@
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效的 IP 地址。
示例 1
输入s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]
* 输入s = "25525511135"
* 输出:["255.255.11.135","255.255.111.35"]
示例 2
输入s = "0000"
输出:["0.0.0.0"]
* 输入s = "0000"
* 输出:["0.0.0.0"]
示例 3
输入s = "1111"
输出:["1.1.1.1"]
* 输入s = "1111"
* 输出:["1.1.1.1"]
示例 4
输入s = "010010"
输出:["0.10.0.10","0.100.1.0"]
* 输入s = "010010"
* 输出:["0.10.0.10","0.100.1.0"]
示例 5
输入s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
* 输入s = "101023"
* 输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
提示:
0 <= s.length <= 3000
s 仅由数字组成
* 0 <= s.length <= 3000
* s 仅由数字组成
## 思路
# 思路
做这道题目之前,最好先把[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)这个做了。
做这道题目之前,最好先把[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)这个做了。
这道题目相信大家刚看的时候,应该会一脸茫然。
其实只要意识到这是切割问题,**切割问题就可以使用回溯搜索法把所有可能性搜出来**,和刚做过的[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)就十分类似了。
其实只要意识到这是切割问题,**切割问题就可以使用回溯搜索法把所有可能性搜出来**,和刚做过的[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)就十分类似了。
切割问题可以抽象为树型结构,如图:
@ -60,7 +60,7 @@ s 仅由数字组成
* 递归参数
在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中我们就提到切割问题类似组合问题。
在[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)中我们就提到切割问题类似组合问题。
startIndex一定是需要的因为不能重复分割记录下一层递归分割的起始位置。
@ -76,7 +76,7 @@ startIndex一定是需要的因为不能重复分割记录下一层递归
* 递归终止条件
终止条件和[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)情况就不同了本题明确要求只会分成4段所以不能用切割线切到最后作为终止条件而是分割的段数作为终止条件。
终止条件和[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)情况就不同了本题明确要求只会分成4段所以不能用切割线切到最后作为终止条件而是分割的段数作为终止条件。
pointNum表示逗点数量pointNum为3说明字符串分成了4段了。
@ -96,7 +96,7 @@ if (pointNum == 3) { // 逗点数量为3时分隔结束
* 单层搜索的逻辑
在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中已经讲过在循环遍历中如何截取子串。
在[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)中已经讲过在循环遍历中如何截取子串。
`for (int i = startIndex; i < s.size(); i++)`循环中 [startIndex, i]这个区间就是截取的子串,需要判断这个子串是否合法。
@ -114,7 +114,7 @@ if (pointNum == 3) { // 逗点数量为3时分隔结束
代码如下:
```
```CPP
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点
@ -138,7 +138,7 @@ for (int i = startIndex; i < s.size(); i++) {
代码如下:
```
```CPP
// 判断字符串s在左闭又闭区间[start, end]所组成的数字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
@ -237,21 +237,21 @@ public:
```
## 总结
# 总结
在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中我列举的分割字符串的难点,本题都覆盖了。
在[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)中我列举的分割字符串的难点,本题都覆盖了。
而且本题还需要操作字符串添加逗号作为分隔符,并验证区间的合法性。
可以说是[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)的加强版。
可以说是[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)的加强版。
在本文的树形结构图中,我已经把详细的分析思路都画了出来,相信大家看了之后一定会思路清晰不少!
## 其他语言版本
# 其他语言版本
java 版本:
## java
```java
class Solution {
@ -308,7 +308,9 @@ class Solution {
}
```
python版本:
## python
python2:
```python
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
@ -338,75 +340,53 @@ class Solution:
backtrack(s, 0)
return res
```
```python
class Solution(object):
def restoreIpAddresses(self, s):
"""
:type s: str
:rtype: List[str]
"""
ans = []
path = []
def backtrack(path, startIndex):
if len(path) == 4:
if startIndex == len(s):
ans.append(".".join(path[:]))
return
for i in range(startIndex+1, min(startIndex+4, len(s)+1)): # 剪枝
string = s[startIndex:i]
if not 0 <= int(string) <= 255:
continue
if not string == "0" and not string.lstrip('0') == string:
continue
path.append(string)
backtrack(path, i)
path.pop()
backtrack([], 0)
return ans```
```
python3:
```python3
class Solution:
def __init__(self) -> None:
self.s = ""
self.res = []
def isVaild(self, s: str) -> bool:
if len(s) > 1 and s[0] == "0":
return False
if 0 <= int(s) <= 255:
return True
return False
def backTrack(self, path: List[str], start: int) -> None:
if start == len(self.s) and len(path) == 4:
self.res.append(".".join(path))
return
for end in range(start + 1, len(self.s) + 1):
# 剪枝
# 保证切割完s没有剩余的字符。
if len(self.s) - end > 3 * (4 - len(path) - 1):
continue
if self.isVaild(self.s[start:end]):
# 在参数处,更新状态,实则创建一个新的变量
# 不会影响当前的状态当前的path变量没有改变
# 因此递归完不用path.pop()
self.backTrack(path + [self.s[start:end]], end)
def __init__(self):
self.result = []
def restoreIpAddresses(self, s: str) -> List[str]:
# prune
if len(s) > 3 * 4:
return []
self.s = s
self.backTrack([], 0)
return self.res
'''
本质切割问题使用回溯搜索法,本题只能切割三次,所以纵向递归总共四层
因为不能重复分割所以需要start_index来记录下一层递归分割的起始位置
添加变量point_num来记录逗号的数量[0,3]
'''
self.result.clear()
if len(s) > 12: return []
self.backtracking(s, 0, 0)
return self.result
def backtracking(self, s: str, start_index: int, point_num: int) -> None:
# Base Case
if point_num == 3:
if self.is_valid(s, start_index, len(s)-1):
self.result.append(s[:])
return
# 单层递归逻辑
for i in range(start_index, len(s)):
# [start_index, i]就是被截取的子串
if self.is_valid(s, start_index, i):
s = s[:i+1] + '.' + s[i+1:]
self.backtracking(s, i+2, point_num+1) # 在填入.后下一子串起始后移2位
s = s[:i+1] + s[i+2:] # 回溯
else:
# 若当前被截取的子串大于255或者大于三位数直接结束本层循环
break
def is_valid(self, s: str, start: int, end: int) -> bool:
if start > end: return False
# 若数字是0开头不合法
if s[start] == '0' and start != end:
return False
if not 0 <= int(s[start:end+1]) <= 255:
return False
return True
```
JavaScript
## JavaScript
```js
/**
@ -435,8 +415,10 @@ var restoreIpAddresses = function(s) {
}
};
```
Go
> 回溯(对于前导 0的IP特别注意s[startIndex]=='0'的判断不应该写成s[startIndex]==0因为s截取出来不是数字
## Go
回溯(对于前导 0的IP特别注意s[startIndex]=='0'的判断不应该写成s[startIndex]==0因为s截取出来不是数字
```go
func restoreIpAddresses(s string) []string {
@ -476,7 +458,7 @@ func isNormalIp(s string,startIndex,end int)bool{
```
C:
## C
```c
//记录结果
char** result;
@ -557,4 +539,4 @@ char ** restoreIpAddresses(char * s, int* returnSize){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -529,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -8,6 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 100. 相同的树
[力扣题目链接](https://leetcode-cn.com/problems/same-tree/)
@ -242,9 +242,9 @@ Go
JavaScript
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -185,7 +185,8 @@ public:
queue<TreeNode*> que;
que.push(root->left); // 将左子树头结点加入队列
que.push(root->right); // 将右子树头结点加入队列
while (!que.empty()) { // 接下来就要判断这这两个树是否相互翻转
while (!que.empty()) { // 接下来就要判断这两个树是否相互翻转
TreeNode* leftNode = que.front(); que.pop();
TreeNode* rightNode = que.front(); que.pop();
if (!leftNode && !rightNode) { // 左节点为空、右节点为空,此时说明是对称的
@ -581,4 +582,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -6,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 二叉树层序遍历登场!
学会二叉树的层序遍历,可以一口气打完以下十题:
@ -20,7 +21,6 @@
* 104.二叉树的最大深度
* 111.二叉树的最小深度
在之前写过这篇文章 [二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)可惜当时只打了5个还不够再给我一次机会我打十个
![我要打十个](https://tva1.sinaimg.cn/large/008eGmZEly1gnadnltbpjg309603w4qp.gif)
@ -87,7 +87,9 @@ public:
python代码
```python3
class Solution:
"""二叉树层序遍历迭代解法"""
@ -113,7 +115,20 @@ class Solution:
return results
```
```python
# 递归法
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
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
@ -406,9 +421,10 @@ var levelOrderBottom = function(root) {
node.left&&queue.push(node.left);
node.right&&queue.push(node.right);
}
res.push(curLevel);
// 从数组前头插入值,避免最后反转数组,减少运算时间
res.unshift(curLevel);
}
return res.reverse();
return res;
};
```
@ -1158,7 +1174,7 @@ public:
if (root != NULL) que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// vector<int> vec;
Node* nodePre;
Node* node;
for (int i = 0; i < size; i++) {
@ -1248,7 +1264,41 @@ class Solution:
first = first.left # 从本层扩展到下一层
return root
```
JavaScript:
```javascript
/**
* // Definition for a Node.
* function Node(val, left, right, next) {
* this.val = val === undefined ? null : val;
* this.left = left === undefined ? null : left;
* this.right = right === undefined ? null : right;
* this.next = next === undefined ? null : next;
* };
*/
/**
* @param {Node} root
* @return {Node}
*/
var connect = function(root) {
if (root === null) return root;
let queue = [root];
while (queue.length) {
let n = queue.length;
for (let i=0; i<n; i++) {
let node = queue.shift();
if (i < n-1) {
node.next = queue[0];
}
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
return root;
};
```
go:
```GO
@ -1411,7 +1461,39 @@ class Solution:
first = dummyHead.next # 此处为换行操作,更新到下一行
return root
```
JavaScript:
```javascript
/**
* // Definition for a Node.
* function Node(val, left, right, next) {
* this.val = val === undefined ? null : val;
* this.left = left === undefined ? null : left;
* this.right = right === undefined ? null : right;
* this.next = next === undefined ? null : next;
* };
*/
/**
* @param {Node} root
* @return {Node}
*/
var connect = function(root) {
if (root === null) {
return null;
}
let queue = [root];
while (queue.length > 0) {
let n = queue.length;
for (let i=0; i<n; i++) {
let node = queue.shift();
if (i < n-1) node.next = queue[0];
if (node.left != null) queue.push(node.left);
if (node.right != null) queue.push(node.right);
}
}
return root;
};
```
go:
```GO
@ -1555,10 +1637,74 @@ class Solution:
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<length;i++{
node:=queue.Remove(queue.Front()).(*TreeNode)
if node.Left!=nil{
queue.PushBack(node.Left)
}
if node.Right!=nil{
queue.PushBack(node.Right)
}
}
ans++//记录深度,其他的是层序遍历的板子
}
return ans
}
```
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 {number}
*/
var maxDepth = function(root) {
// 最大的深度就是二叉树的层数
if (root === null) return 0;
let queue = [root];
let height = 0;
while (queue.length) {
let n = queue.length;
height++;
for (let i=0; i<n; i++) {
let node = queue.shift();
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
return height;
};
```
# 111.二叉树的最小深度
@ -1659,7 +1805,78 @@ class Solution:
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<length;i++{
node:=queue.Remove(queue.Front()).(*TreeNode)
if node.Left==nil&&node.Right==nil{//当前节点没有左右节点,则代表此层是最小层
return ans+1//返回当前层 ans代表是上一层
}
if node.Left!=nil{
queue.PushBack(node.Left)
}
if node.Right!=nil{
queue.PushBack(node.Right)
}
}
ans++//记录层数
}
return ans+1
}
```
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 {number}
*/
var minDepth = function(root) {
if (root === null) return 0;
let queue = [root];
let deepth = 0;
while (queue.length) {
let n = queue.length;
deepth++;
for (let i=0; i<n; i++) {
let node = queue.shift();
// 如果左右节点都是null则该节点深度最小
if (node.left === null && node.right === null) {
return deepth;
}
node.left && queue.push(node.left);;
node.right && queue.push (node.right);
}
}
return deepth;
};
```
@ -1689,4 +1906,4 @@ JavaScript
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -42,14 +42,14 @@
1. 确定递归函数的参数和返回值参数就是传入树的根节点返回就返回这棵树的深度所以返回值为int类型。
代码如下:
```c++
```CPP
int getdepth(treenode* node)
```
2. 确定终止条件如果为空节点的话就返回0表示高度为0。
代码如下:
```c++
```CPP
if (node == null) return 0;
```
@ -57,7 +57,7 @@ if (node == null) return 0;
代码如下:
```c++
```CPP
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
@ -66,7 +66,7 @@ return depth;
所以整体c++代码如下:
```c++
```CPP
class solution {
public:
int getdepth(treenode* node) {
@ -83,7 +83,7 @@ public:
```
代码精简之后c++代码如下:
```c++
```CPP
class solution {
public:
int maxdepth(treenode* root) {
@ -99,7 +99,7 @@ public:
本题当然也可以使用前序,代码如下:(**充分表现出求深度回溯的过程**)
```c++
```CPP
class solution {
public:
int result;
@ -122,7 +122,7 @@ public:
}
int maxdepth(treenode* root) {
result = 0;
if (root == 0) return result;
if (root == NULL) return result;
getdepth(root, 1);
return result;
}
@ -133,7 +133,7 @@ public:
注意以上代码是为了把细节体现出来,简化一下代码如下:
```c++
```CPP
class solution {
public:
int result;
@ -171,7 +171,7 @@ public:
c++代码如下:
```c++
```CPP
class solution {
public:
int maxdepth(treenode* root) {
@ -218,7 +218,7 @@ public:
c++代码:
```c++
```CPP
class solution {
public:
int maxdepth(node* root) {
@ -235,7 +235,7 @@ public:
依然是层序遍历,代码如下:
```c++
```CPP
class solution {
public:
int maxdepth(node* root) {
@ -586,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -607,6 +607,7 @@ class Solution {
for (int i = inLeft; i < inRight; i++) {
if (inorder[i] == rootVal) {
rootIndex = i;
break;
}
}
// 根据rootIndex划分左右子树
@ -897,4 +898,4 @@ struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int in
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -9,7 +9,7 @@
> 构造二叉搜索树,一不小心就平衡了
## 108.将有序数组转换为二叉搜索树
# 108.将有序数组转换为二叉搜索树
[力扣题目链接](https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/)
@ -21,7 +21,7 @@
![108.将有序数组转换为二叉搜索树](https://img-blog.csdnimg.cn/20201022164420763.png)
## 思路
# 思路
做这道题目之前大家可以了解一下这几道:
@ -192,7 +192,7 @@ public:
};
```
## 总结
# 总结
**在[二叉树:构造二叉树登场!](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,7 +304,9 @@ class Solution {
}
```
Python
## Python
**递归**
```python3
# Definition for a binary tree node.
# class TreeNode:
@ -311,36 +314,39 @@ Python
# self.val = val
# self.left = left
# self.right = right
#递归法
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def buildaTree(left,right):
if left > right: return None #左闭右闭的区间,当区间 left > right的时候就是空节点,当left = right的时候不为空
mid = left + (right - left) // 2 #保证数据不会越界
val = nums[mid]
root = TreeNode(val)
root.left = buildaTree(left,mid - 1)
root.right = buildaTree(mid + 1,right)
return root
root = buildaTree(0,len(nums) - 1) #左闭右闭区间
'''
构造二叉树:重点是选取数组最中间元素为分割点,左侧是递归左区间;右侧是递归右区间
必然是平衡树
左闭右闭区间
'''
# 返回根节点
root = self.traversal(nums, 0, len(nums)-1)
return root
def traversal(self, nums: List[int], left: int, right: int) -> TreeNode:
# Base Case
if left > right:
return None
# 确定左右界的中心,防越界
mid = left + (right - left) // 2
# 构建根节点
mid_root = TreeNode(nums[mid])
# 构建以左右界的中心为分割点的左右子树
mid_root.left = self.traversal(nums, left, mid-1)
mid_root.right = self.traversal(nums, mid+1, right)
# 返回由被传入的左右界定义的某子树的根节点
return mid_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 +356,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 +382,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -125,9 +125,10 @@ public:
1. 明确递归函数的参数和返回值
参数的话为传入节点指针,就没有其他参数需要传递了,返回值要返回传入节点为根节点树的深度
参数:当前传入节点。
返回值:以当前传入节点为根节点的树的高度。
那么如何标记左右子树是否差值大于1呢
那么如何标记左右子树是否差值大于1呢
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
@ -136,9 +137,9 @@ public:
代码如下:
```
```CPP
// -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度
int getDepth(TreeNode* node)
int getHeight(TreeNode* node)
```
2. 明确终止条件
@ -147,7 +148,7 @@ int getDepth(TreeNode* node)
代码如下:
```
```CPP
if (node == NULL) {
return 0;
}
@ -155,23 +156,23 @@ if (node == NULL) {
3. 明确单层递归的逻辑
如何判断当前传入节点为根节点的二叉树是否是平衡二叉树呢当然是左子树高度和右子树高度相差
如何判断当前传入节点为根节点的二叉树是否是平衡二叉树呢当然是左子树高度和右子树高度的差值
分别求出左右子树的高度然后如果差值小于等于1则返回当前二叉树的高度否则则返回-1表示已经不是二叉树了。
分别求出左右子树的高度然后如果差值小于等于1则返回当前二叉树的高度否则则返回-1表示已经不是二叉平衡树了。
代码如下:
```CPP
int leftDepth = depth(node->left); // 左
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right); // 右
if (rightDepth == -1) return -1;
int leftHeight = getHeight(node->left); // 左
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right); // 右
if (rightHeight == -1) return -1;
int result;
if (abs(leftDepth - rightDepth) > 1) { // 中
if (abs(leftHeight - rightHeight) > 1) { // 中
result = -1;
} else {
result = 1 + max(leftDepth, rightDepth); // 以当前节点为根节点的最大高度
result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度
}
return result;
@ -180,27 +181,27 @@ return result;
代码精简之后如下:
```CPP
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1;
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
```
此时递归的函数就已经写出来了,这个递归的函数传入节点指针,返回以该节点为根节点的二叉树的高度,如果不是二叉平衡树,则返回-1。
getDepth整体代码如下:
getHeight整体代码如下:
```CPP
int getDepth(TreeNode* node) {
int getHeight(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1;
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}
```
@ -210,18 +211,18 @@ int getDepth(TreeNode* node) {
class Solution {
public:
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
int getDepth(TreeNode* node) {
int getHeight(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1; // 说明左子树已经不是二叉平衡树
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1; // 说明右子树已经不是二叉平衡树
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}
bool isBalanced(TreeNode* root) {
return getDepth(root) == -1 ? false : true;
return getHeight(root) == -1 ? false : true;
}
};
```
@ -498,20 +499,35 @@ 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 isBalanced(self, root: TreeNode) -> bool:
return True if self.getDepth(root) != -1 else False
if self.get_height(root) != -1:
return True
else:
return False
#返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
def getDepth(self, node):
if not node:
def get_height(self, root: TreeNode) -> int:
# Base Case
if not root:
return 0
leftDepth = self.getDepth(node.left)
if leftDepth == -1: return -1 #说明左子树已经不是二叉平衡树
rightDepth = self.getDepth(node.right)
if rightDepth == -1: return -1 #说明右子树已经不是二叉平衡树
return -1 if abs(leftDepth - rightDepth)>1 else 1 + max(leftDepth, rightDepth)
# 左
if (left_height := self.get_height(root.left)) == -1:
return -1
# 右
if (right_height := self.get_height(root.right)) == -1:
return -1
# 中
if abs(left_height - right_height) > 1:
return -1
else:
return 1 + max(left_height, right_height)
```
迭代法:
@ -624,4 +640,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -411,6 +411,31 @@ class solution {
}
```
```java
// 解法2
class Solution {
List<List<Integer>> result;
LinkedList<Integer> path;
public List<List<Integer>> 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.路径总和
@ -686,4 +711,4 @@ 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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -274,4 +274,4 @@ const numDistinct = (s, t) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 116. 填充每个节点的下一个右侧节点指针
[力扣题目链接](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/)
@ -255,9 +255,9 @@ const connect = root => {
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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 { // 动态规划解法
@ -315,6 +335,8 @@ func max(a,b int)int {
JavaScript
> 动态规划
```javascript
const maxProfit = prices => {
const len = prices.length;
@ -333,11 +355,23 @@ const maxProfit = prices => {
};
```
> 贪心法
```javascript
var maxProfit = function(prices) {
let lowerPrice = prices[0];// 重点是维护这个最小值(贪心的思想)
let profit = 0;
for(let i = 0; i < prices.length; i++){
lowerPrice = Math.min(lowerPrice, prices[i]);// 贪心地选择左面的最小价格
profit = Math.max(profit, prices[i] - lowerPrice);// 遍历一趟就可以获得最大利润
}
return profit;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -52,7 +52,7 @@
**如果想到其实最终利润是可以分解的,那么本题就很容易了!**
分解呢?
分解呢?
假如第0天买入第3天卖出那么利润为prices[3] - prices[0]。
@ -131,9 +131,9 @@ public:
一旦想到这里了,很自然就会想到贪心了,即:只收集每天的正利润,最后稳稳的就是最大利润了。
## 其他语言版本
# 其他语言版本
Java
## Java
```java
// 贪心思路
@ -171,7 +171,7 @@ class Solution { // 动态规划
Python
## Python
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
@ -181,7 +181,21 @@ class Solution:
return result
```
Go
python动态规划
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
length = len(prices)
dp = [[0] * 2 for _ in range(length)]
dp[0][0] = -prices[0]
dp[0][1] = 0
for i in range(1, length):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) #注意这里是和121. 买卖股票的最佳时机唯一不同的地方
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
return dp[-1][1]
```
## Go
```golang
//贪心算法
func maxProfit(prices []int) int {
@ -217,9 +231,9 @@ func maxProfit(prices []int) int {
}
```
Javascript:
## Javascript
贪心
```Javascript
// 贪心
var maxProfit = function(prices) {
let result = 0
for(let i = 1; i < prices.length; i++) {
@ -229,7 +243,31 @@ var maxProfit = function(prices) {
};
```
C:
动态规划
```javascript
const maxProfit = (prices) => {
let dp = Array.from(Array(prices.length), () => Array(2).fill(0));
// dp[i][0] 表示第i天持有股票所得现金。
// dp[i][1] 表示第i天不持有股票所得最多现金
dp[0][0] = 0 - prices[0];
dp[0][1] = 0;
for(let i = 1; i < prices.length; i++) {
// 如果第i天持有股票即dp[i][0] 那么可以由两个状态推出来
// 第i-1天就持有股票那么就保持现状所得现金就是昨天持有股票的所得现金 即dp[i - 1][0]
// 第i天买入股票所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即dp[i - 1][1] - prices[i]
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i]);
// 在来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来
// 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1]
// 第i天卖出股票所得现金就是按照今天股票佳价格卖出后所得现金即prices[i] + dp[i - 1][0]
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
}
return dp[prices.length -1][0];
};
```
## C
```c
int maxProfit(int* prices, int pricesSize){
int result = 0;
@ -248,4 +286,4 @@ int maxProfit(int* prices, int pricesSize){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -202,6 +202,7 @@ Go
Javascript
```javascript
// 方法一动态规划dp 数组)
const maxProfit = (prices) => {
let dp = Array.from(Array(prices.length), () => Array(2).fill(0));
// dp[i][0] 表示第i天持有股票所得现金。
@ -222,6 +223,21 @@ const maxProfit = (prices) => {
return dp[prices.length -1][0];
};
// 方法二:动态规划(滚动数组)
const maxProfit = (prices) => {
// 滚动数组
// have: 第i天持有股票最大收益; notHave: 第i天不持有股票最大收益
let n = prices.length,
have = -prices[0],
notHave = 0;
for (let i = 1; i < n; i++) {
have = Math.max(have, notHave - prices[i]);
notHave = Math.max(notHave, have + prices[i]);
}
// 最终手里不持有股票才能保证收益最大化
return notHave;
}
```
@ -230,4 +246,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -324,4 +324,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 127. 单词接龙
[力扣题目链接](https://leetcode-cn.com/problems/word-ladder/)
@ -141,10 +141,44 @@ public int ladderLength(String beginWord, String endWord, List<String> wordList)
## Go
## JavaScript
```javascript
var ladderLength = function(beginWord, endWord, wordList) {
// 将wordList转成Set提高查询速度
const wordSet = new Set(wordList);
// Set元素个数为0 或者 endWord没有在wordSet出现直接返回0
if (wordSet.size === 0 || !wordSet.has(endWord)) return 0;
// 记录word是否访问过
const visitMap = new Map();// <word, 查询到这个word路径长度>
// 初始化队列
const queue = [];
queue.push(beginWord);
// 初始化visitMap
visitMap.set(beginWord, 1);
while(queue.length !== 0){
let word = queue.shift(); // 删除队首元素,将它的值存放在word
let path = visitMap.get(word); // 这个word的路径长度
for(let i = 0; i < word.length; i++){ // 遍历单词的每个字符
for (let c = 97; c <= 122; c++) { // 对应26个字母ASCII值 从'a' 到 'z' 遍历替换
// 拼串得到新的字符串
let newWord = word.slice(0, i) + String.fromCharCode(c) + word.slice(i + 1);
if(newWord === endWord) return path + 1; // 找到了end返回path+1
// wordSet出现了newWord并且newWord没有被访问过
if(wordSet.has(newWord) && !visitMap.has(newWord)) {
// 添加访问信息
visitMap.set(newWord, path + 1);
queue.push(newWord);
}
}
}
}
return 0;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,7 +1,15 @@
## 链接
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 129. 求根节点到叶节点数字之和
[力扣题目链接](https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/)
## 思路
# 思路
本题和[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-路径总和) 做了。
@ -15,7 +23,7 @@
那么先按递归三部曲来分析:
### 递归三部曲
## 递归三部曲
如果对递归三部曲不了解的话,可以看这里:[二叉树:前中后递归详解](https://programmercarl.com/二叉树的递归遍历.html)
@ -107,7 +115,7 @@ path.pop_back(); // 回溯
```
**把回溯放在花括号外面了,世界上最遥远的距离,是你在花括号里,而我在花括号外!** 这就不对了。
### 整体C++代码
## 整体C++代码
关键逻辑分析完了整体C++代码如下:
@ -160,10 +168,56 @@ public:
**我这里提供的代码把整个回溯过程充分体现出来,希望可以帮助大家看的明明白白!**
## 其他语言版本
# 其他语言版本
Java
```java
class Solution {
List<Integer> 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<Integer> path){
int sum = 0;
for (Integer num:path){
// sum * 10 表示进位
sum = sum * 10 + num;
}
return sum;
}
}
```
Python
```python3
class Solution:
@ -194,6 +248,48 @@ class Solution:
Go
JavaScript
```javascript
var sumNumbers = function(root) {
const listToInt = path => {
let sum = 0;
for(let num of path){
// sum * 10 表示进位
sum = sum * 10 + num;
}
return sum;
}
const recur = root =>{
if (root.left == null && root.right == null) {
// 当是叶子节点的时候,开始处理
res += listToInt(path);
return;
}
if (root.left != null){
// 注意有回溯
path.push(root.left.val);
recur(root.left);
path.pop();
}
if (root.right != null){
// 注意有回溯
path.push(root.right.val);
recur(root.right);
path.pop();
}
return;
};
const path = new Array();
let res = 0;
// 如果节点为0那么就返回0
if (root == null) return 0;
// 首先将根节点放到集合中
path.push(root.val);
// 开始递归
recur(root);
return res;
};
```
@ -201,4 +297,4 @@ JavaScript
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -9,7 +9,7 @@
> 切割问题其实是一种组合问题!
## 131.分割回文串
# 131.分割回文串
[力扣题目链接](https://leetcode-cn.com/problems/palindrome-partitioning/)
@ -26,7 +26,7 @@
]
## 思路
# 思路
关于本题大家也可以看我在B站的视频讲解[131.分割回文串B站视频](https://www.bilibili.com/video/BV1c54y1e7k6)
@ -210,7 +210,7 @@ public:
};
```
## 总结
# 总结
这道题目在leetcode上是中等但可以说是hard的题目了但是代码其实就是按照模板的样子来的。
@ -244,12 +244,10 @@ public:
# 其他语言版本
## 其他语言版本
Java
## Java
```Java
class Solution {
List<List<String>> lists = new ArrayList<>();
@ -291,59 +289,93 @@ class Solution {
}
```
Python
```python
# 版本一
## Python
**回溯+正反序判断回文串**
```python3
class Solution:
def partition(self, s: str) -> List[List[str]]:
res = []
path = [] #放已经回文的子串
def backtrack(s,startIndex):
if startIndex >= len(s): #如果起始位置已经大于s的大小说明已经找到了一组分割方案了
return res.append(path[:])
for i in range(startIndex,len(s)):
p = s[startIndex:i+1] #获取[startIndex,i+1]在s中的子串
if p == p[::-1]: path.append(p) #是回文子串
else: continue #不是回文,跳过
backtrack(s,i+1) #寻找i+1为起始位置的子串
path.pop() #回溯过程弹出本次已经填在path的子串
backtrack(s,0)
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切片是披着值类型外衣的引用类型
def __init__(self):
self.paths = []
self.path = []
def partition(self, s: str) -> List[List[str]]:
'''
递归用于纵向遍历
for循环用于横向遍历
当切割线迭代至字符串末尾,说明找到一种方法
类似组合问题为了不重复切割同一位置需要start_index来做标记下一轮递归的起始位置(切割线)
'''
self.path.clear()
self.paths.clear()
self.backtracking(s, 0)
return self.paths
def backtracking(self, s: str, start_index: int) -> None:
# Base Case
if start_index >= len(s):
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(s)):
# 此次比其他组合题目多了一步判断:
# 判断被截取的这一段子串([start_index, i])是否为回文串
temp = s[start_index:i+1]
if temp == temp[::-1]: # 若反序和正序相同,意味着这是回文串
self.path.append(temp)
self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
self.path.pop()
else:
continue
```
**回溯+函数判断回文串**
```python3
class Solution:
def __init__(self):
self.paths = []
self.path = []
def partition(self, s: str) -> List[List[str]]:
'''
递归用于纵向遍历
for循环用于横向遍历
当切割线迭代至字符串末尾,说明找到一种方法
类似组合问题为了不重复切割同一位置需要start_index来做标记下一轮递归的起始位置(切割线)
'''
self.path.clear()
self.paths.clear()
self.backtracking(s, 0)
return self.paths
def backtracking(self, s: str, start_index: int) -> None:
# Base Case
if start_index >= len(s):
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(s)):
# 此次比其他组合题目多了一步判断:
# 判断被截取的这一段子串([start_index, i])是否为回文串
if self.is_palindrome(s, start_index, i):
self.path.append(s[start_index:i+1])
self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
self.path.pop() # 回溯
else:
continue
def is_palindrome(self, s: str, start: int, end: int) -> bool:
i: int = start
j: int = end
while i < j:
if s[i] != s[j]:
return False
i += 1
j -= 1
return True
```
## Go
**注意切片go切片是披着值类型外衣的引用类型**
```go
func partition(s string) [][]string {
var tmpString []string//切割字符串集合
@ -387,7 +419,7 @@ func isPartition(s string,startIndex,end int)bool{
}
```
javaScript
## javaScript
```js
/**
@ -425,4 +457,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -8,6 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 132. 分割回文串 II
[力扣题目链接](https://leetcode-cn.com/problems/palindrome-partitioning-ii/)
@ -142,7 +142,8 @@ for (int i = 1; i < s.size(); i++) {
就是其实这两道题目的代码:
* 647.回文子串
* [647. 回文子串](https://programmercarl.com/0647.回文子串.html)
* 5.最长回文子串
所以先用一个二维数组来保存整个字符串的回文情况。
@ -246,11 +247,47 @@ class Solution:
## JavaScript
```js
var minCut = function(s) {
const len = s.length;
// 二维数组isPalindromic来保存整个字符串的回文情况
const isPalindromic = new Array(len).fill(false).map(() => new Array(len).fill(false));
for(let i = len - 1; i >= 0; i--){
for(let j = i; j < len; j++){
if(s[i] === s[j] && (j - i <= 1 || isPalindromic[i + 1][j - 1])){
isPalindromic[i][j] = true;
}
}
}
// dp[i]:范围是[0, i]的回文子串最少分割次数是dp[i]
const dp = new Array(len).fill(0);
for(let i = 0; i < len; i++) dp[i] = i; // 初始化 dp[i]的最大值其实就是i也就是把每个字符分割出来
for(let i = 1; i < len; i++){
if(isPalindromic[0][i]){ // 判断是不是回文子串
dp[i] = 0;
continue;
}
/*
如果要对长度为[0, i]的子串进行分割分割点为j。
那么如果分割后,区间[j + 1, i]是回文子串那么dp[i] 就等于 dp[j] + 1。
这里可能有同学就不明白了,为什么只看[j + 1, i]区间,不看[0, j]区间是不是回文子串呢?
那么在回顾一下dp[i]的定义: 范围是[0, i]的回文子串最少分割次数是dp[i]。
[0, j]区间的最小切割数量我们已经知道了就是dp[j]。
此时就找到了递推关系当切割点j在[0, i] 之间时候dp[i] = dp[j] + 1;
本题是要找到最少分割次数所以遍历j的时候要取最小的dp[i]。dp[i] = Math.min(dp[i], dp[j] + 1);
*/
for(let j = 0; j < i; j++){
if(isPalindromic[j + 1][i]){
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[len - 1];
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -281,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
@ -337,4 +379,4 @@ int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -132,30 +132,33 @@ public:
Java
```java
class Solution {
/**
分两个阶段
1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1
2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,此时 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值,这样才符合 它比它左边的大,也比它右边大
*/
public int candy(int[] ratings) {
int[] candy = new int[ratings.length];
for (int i = 0; i < candy.length; i++) {
candy[i] = 1;
}
int[] candyVec = new int[ratings.length];
candyVec[0] = 1;
for (int i = 1; i < ratings.length; i++) {
if (ratings[i] > ratings[i - 1]) {
candy[i] = candy[i - 1] + 1;
candyVec[i] = candyVec[i - 1] + 1;
} else {
candyVec[i] = 1;
}
}
for (int i = ratings.length - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candy[i] = Math.max(candy[i],candy[i + 1] + 1);
candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1);
}
}
int count = 0;
for (int i = 0; i < candy.length; i++) {
count += candy[i];
int ans = 0;
for (int s : candyVec) {
ans += s;
}
return count;
return ans;
}
}
```
@ -242,4 +245,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 141. 环形链表
给定一个链表,判断链表中是否有环。
@ -114,11 +114,22 @@ class Solution:
## JavaScript
```js
var hasCycle = function(head) {
let fast = head;
let slow = head;
// 空链表、单节点链表一定不会有环
while(fast != null && fast.next != null){
fast = fast.next.next; // 快指针,一次移动两步
slow = slow.next; // 慢指针,一次移动一步
if(fast === slow) return true; // 快慢指针相遇,表明有环
}
return false; // 正常走到链表末尾,表明没有环
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -334,4 +334,4 @@ extension ListNode: Equatable {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -50,10 +50,6 @@ public:
cur = cur->next;
count++;
}
if (vec.size() % 2 == 0) { // 如果是偶数,还要多处理中间的一个
cur->next = vec[i];
cur = cur->next;
}
cur->next = nullptr; // 注意结尾
}
};
@ -177,6 +173,7 @@ public:
Java
```java
// 方法三
public class ReorderList {
public void reorderList(ListNode head) {
ListNode fast = head, slow = head;
@ -219,6 +216,68 @@ public class ReorderList {
return headNode.next;
}
}
// 方法一 Java实现使用数组存储节点
class Solution {
public void reorderList(ListNode head) {
// 双指针的做法
ListNode cur = head;
// ArrayList底层是数组可以使用下标随机访问
List<ListNode> 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++;
}
// 注意结尾要结束一波
cur.next = null;
}
}
// 方法二:使用双端队列,简化了数组的操作,代码相对于前者更简洁(避免一些边界条件)
class Solution {
public void reorderList(ListNode head) {
// 使用双端队列的方法来解决
Deque<ListNode> 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
@ -280,12 +339,111 @@ class Solution:
Go
JavaScript
```javascript
// 方法一 使用数组存储节点
var reorderList = function(head, s = [], tmp) {
let cur = head;
// list是数组可以使用下标随机访问
const list = [];
while(cur != null){
list.push(cur);
cur = cur.next;
}
cur = head; // 重新回到头部
let l = 1, r = list.length - 1; // 注意左边是从1开始
let count = 0;
while(l <= r){
if(count % 2 == 0){
// even
cur.next = list[r];
r--;
} else {
// odd
cur.next = list[l];
l++;
}
// 每一次指针都需要移动
cur = cur.next;
count++;
}
// 注意结尾要结束一波
cur.next = null;
}
// 方法二 使用双端队列的方法来解决 js中运行很慢
var reorderList = function(head, s = [], tmp) {
// js数组作为双端队列
const deque = [];
// 这里是取head的下一个节点head不需要再入队了避免造成重复
let cur = head.next;
while(cur != null){
deque.push(cur);
cur = cur.next;
}
cur = head; // 回到头部
let count = 0;
while(deque.length !== 0){
if(count % 2 == 0){
// even取出队列右边尾部的值
cur.next = deque.pop();
} else {
// odd, 取出队列左边头部的值
cur.next = deque.shift();
}
cur = cur.next;
count++;
}
cur.next = null;
}
//方法三 将链表分割成两个链表,然后把第二个链表反转,之后在通过两个链表拼接成新的链表
var reorderList = function(head, s = [], tmp) {
const reverseList = head => {
let headNode = new ListNode(0);
let cur = head;
let next = null;
while(cur != null){
next = cur.next;
cur.next = headNode.next;
headNode.next = cur;
cur = next;
}
return headNode.next;
}
let fast = head, slow = head;
//求出中点
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
//right就是右半部分 12345 就是45 1234 就是34
let right = slow.next;
//断开左部分和右部分
slow.next = null;
//反转右部分 right就是反转后右部分的起点
right = reverseList(right);
//左部分的起点
let left = head;
//进行左右部分来回连接
//这里左部分的节点个数一定大于等于右部分的节点个数 因此只判断right即可
while (right != null) {
let curLeft = left.next;
left.next = right;
left = curLeft;
let curRight = right.next;
right.next = left;
right = curRight;
}
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -248,4 +248,4 @@ class Solution:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -553,4 +553,4 @@ func reverseWord(_ s: inout [Character]) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,3 +1,14 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
同:[链表:链表相交](https://programmercarl.com/面试题02.07.链表相交.html)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -280,6 +280,7 @@ Go
Javascript
```javascript
// 方法一:动态规划
const maxProfit = (k,prices) => {
if (prices == null || prices.length < 2 || k == 0) {
return 0;
@ -300,10 +301,34 @@ const maxProfit = (k,prices) => {
return dp[prices.length - 1][2 * k];
};
// 方法二:动态规划+空间优化
var maxProfit = function(k, prices) {
let n = prices.length;
let dp = new Array(2*k+1).fill(0);
// dp 买入状态初始化
for (let i = 1; i <= 2*k; i += 2) {
dp[i] = - prices[0];
}
for (let i = 1; i < n; i++) {
for (let j = 1; j < 2*k+1; j++) {
// j 为奇数:买入状态
if (j % 2) {
dp[j] = Math.max(dp[j], dp[j-1] - prices[i]);
} else {
// j为偶数卖出状态
dp[j] = Math.max(dp[j], dp[j-1] + prices[i]);
}
}
}
return dp[2*k];
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 189. 旋转数组
给定一个数组将数组中的元素向右移动 k 个位置其中 k 是非负数。
@ -149,10 +149,10 @@ var rotate = 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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -198,4 +198,4 @@ const rob = nums => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -159,26 +159,28 @@ func getSum(n int) int {
javaScript:
```js
function getN(n) {
if (n == 1 || n == 0) return n;
let res = 0;
while (n) {
res += (n % 10) * (n % 10);
n = parseInt(n / 10);
var isHappy = function (n) {
let m = new Map()
const getSum = (num) => {
let sum = 0
while (n) {
sum += (n % 10) ** 2
n = Math.floor(n / 10)
}
return sum
}
while (true) {
// n出现过证明已陷入无限循环
if (m.has(n)) return false
if (n === 1) return true
m.set(n, 1)
n = getSum(n)
}
return res;
}
var isHappy = function(n) {
const sumSet = new Set();
while (n != 1 && !sumSet.has(n)) {
sumSet.add(n);
n = getN(n);
}
return n == 1;
};
// 使用环形链表的思想 说明出现闭环 退出循环
// 方法二:使用环形链表的思想 说明出现闭环 退出循环
var isHappy = function(n) {
if (getN(n) == 1) return true;
let a = getN(n), b = getN(getN(n));
@ -189,6 +191,30 @@ var isHappy = function(n) {
}
return b === 1 || getN(b) === 1 ;
};
// 方法三使用Set()更简洁
/**
* @param {number} n
* @return {boolean}
*/
var getSum = function (n) {
let sum = 0;
while (n) {
sum += (n % 10) ** 2;
n = Math.floor(n/10);
}
return sum;
}
var isHappy = function(n) {
let set = new Set(); // Set() 里的数是惟一的
// 如果在循环中某个值重复出现,说明此时陷入死循环,也就说明这个值不是快乐数
while (n !== 1 && !set.has(n)) {
set.add(n);
n = getSum(n);
}
return n === 1;
};
```
Swift
@ -258,4 +284,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -40,7 +40,7 @@
**当然如果使用java python的话就不用手动管理内存了。**
还要说明一下就算使用C++来做leetcode如果移除一个节点之后没有手动在内存中删除这个节点leetcode依然也是可以通过的只不过内存使用的空间大一些而已但建议依然要养手动清理内存的习惯。
还要说明一下就算使用C++来做leetcode如果移除一个节点之后没有手动在内存中删除这个节点leetcode依然也是可以通过的只不过内存使用的空间大一些而已但建议依然要养手动清理内存的习惯。
这种情况下的移除操作就是让节点next指针直接指向下下一个节点就可以了
@ -362,4 +362,4 @@ func removeElements(head *ListNode, val int) *ListNode {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 205. 同构字符串
[力扣题目链接](https://leetcode-cn.com/problems/isomorphic-strings/)
@ -119,11 +119,30 @@ func isIsomorphic(s string, t string) bool {
## JavaScript
```js
var isIsomorphic = function(s, t) {
let len = s.length;
if(len === 0) return true;
let maps = new Map();
let mapt = new Map();
for(let i = 0, j = 0; i < len; i++, j++){
if(!maps.has(s[i])){
maps.set(s[i],t[j]);// maps保存 s[i] 到 t[j]的映射
}
if(!mapt.has(t[j])){
mapt.set(t[j],s[i]);// mapt保存 t[j] 到 s[i]的映射
}
// 无法映射,返回 false
if(maps.get(s[i]) !== t[j] || mapt.get(t[j]) !== s[i]){
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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -27,9 +27,9 @@
![206_反转链表](https://img-blog.csdnimg.cn/20210218090901207.png)
之前链表的头节点是元素1 反转之后头结点就是元素5 ,这里并没有添加或者删除节点,仅仅是改next指针的方向。
之前链表的头节点是元素1 反转之后头结点就是元素5 ,这里并没有添加或者删除节点,仅仅是改next指针的方向。
那么接下来看一看是如何反转呢?
那么接下来看一看是如何反转呢?
我们拿有示例中的链表来举例,如动画所示:
@ -96,6 +96,28 @@ public:
};
```
我们可以发现,上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向。
具体代码如下(带详细注释):
```CPP
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// 边缘条件判断
if(head == NULL) return NULL;
if (head->next == NULL) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode *last = reverseList(head->next);
// 翻转头节点与第二个节点的指向
head->next->next = head;
// 此时的 head 节点为尾节点next 需要指向 NULL
head->next = NULL;
return last;
}
};
```
## 其他语言版本
@ -135,13 +157,32 @@ class Solution {
temp = cur.next;// 先保存下一个节点
cur.next = prev;// 反转
// 更新prev、cur位置
prev = cur;
cur = temp;
return reverse(prev, cur);
// prev = cur;
// cur = temp;
return reverse(cur, temp);
}
}
```
```java
// 从后向前递归
class Solution {
ListNode reverseList(ListNode head) {
// 边缘条件判断
if(head == null) return null;
if (head.next == null) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode last = reverseList(head.next);
// 翻转头节点与第二个节点的指向
head.next.next = head;
// 此时的 head 节点为尾节点next 需要指向 NULL
head.next = null;
return last;
}
}
```
Python迭代法
```python
#双指针
@ -414,4 +455,4 @@ struct ListNode* reverseList(struct ListNode* head){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -297,4 +297,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -30,7 +30,7 @@
输出: [[1,2,6], [1,3,5], [2,3,4]]
## 思路
# 思路
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
@ -180,7 +180,7 @@ if (sum > targetSum) { // 剪枝操作
最后C++代码如下:
```c++
```CPP
class Solution {
private:
vector<vector<int>> result; // 存放结果集
@ -223,10 +223,10 @@ public:
## 其他语言版本
# 其他语言版本
Java
## Java
模板方法
```java
@ -299,7 +299,8 @@ class Solution {
}
```
Python
## Python
```py
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
@ -320,10 +321,9 @@ class Solution:
return res
```
Go
## Go
> 回溯+减枝
回溯+减枝
```go
func combinationSum3(k int, n int) [][]int {
@ -353,7 +353,7 @@ func backTree(n,k,startIndex int,track *[]int,result *[][]int){
}
```
javaScript:
## javaScript:
```js
// 等差数列
@ -454,4 +454,4 @@ int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -204,7 +204,27 @@ class Solution {
}
}
```
```java
class Solution {
// 迭代法
public int countNodes(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> 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 {
/**
@ -435,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -112,7 +112,7 @@ public:
# 优化
其实这道题目就是用一个队就够了。
其实这道题目就是用一个队就够了。
**一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。**
@ -538,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -363,7 +363,9 @@ class Solution:
return root
```
### Go
### Go
递归版本的前序遍历
```Go
func invertTree(root *TreeNode) *TreeNode {
@ -381,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<length;i++{
e:=queue.Remove(queue.Front()).(*TreeNode)
e.Left,e.Right=e.Right,e.Left//交换
if e.Left!=nil{
queue.PushBack(e.Left)
}
if e.Right!=nil{
queue.PushBack(e.Right)
}
}
}
return root
}
```
### JavaScript
使用递归版本的前序遍历
@ -522,4 +614,4 @@ struct TreeNode* invertTree(struct TreeNode* root){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -356,4 +356,4 @@ 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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -7,6 +6,7 @@
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 234.回文链表
[力扣题目链接](https://leetcode-cn.com/problems/palindrome-linked-list/)
@ -284,13 +284,47 @@ class Solution:
## JavaScript
```js
var isPalindrome = function(head) {
const reverseList = head => {// 反转链表
let temp = null;
let pre = null;
while(head != null){
temp = head.next;
head.next = pre;
pre = head;
head = temp;
}
return pre;
}
// 如果为空或者仅有一个节点返回true
if(!head && !head.next) return true;
let slow = head;
let fast = head;
let pre = head;
while(fast != null && fast.next != null){
pre = slow; // 记录slow的前一个结点
slow = slow.next;
fast = fast.next.next;
}
pre.next = null; // 分割两个链表
// 前半部分
let cur1 = head;
// 后半部分。这里使用了反转链表
let cur2 = reverseList(slow);
while(cur1 != null){
if(cur1.val != cur2.val) return false;
// 注意要移动两个结点
cur1 = cur1.next;
cur2 = cur2.next;
}
return true;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -234,7 +234,15 @@ public:
## 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
@ -260,17 +268,30 @@ class Solution {
递归法:
```python
class Solution:
"""二叉搜索树的最近公共祖先 递归法"""
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root: return root //
if root.val >p.val and root.val > q.val:
return self.lowestCommonAncestor(root.left,p,q) //
elif root.val < p.val and root.val < q.val:
return self.lowestCommonAncestor(root.right,p,q) //
else: return root
if root.val > p.val and root.val > q.val:
return self.lowestCommonAncestor(root.left, p, q)
if root.val < p.val and root.val < q.val:
return self.lowestCommonAncestor(root.right, p, q)
return root
```
迭代法:
```python
class Solution:
"""二叉搜索树的最近公共祖先 迭代法"""
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
while True:
if root.val > p.val and root.val > q.val:
root = root.left
elif root.val < p.val and root.val < q.val:
root = root.right
else:
return root
```
## Go
@ -336,4 +357,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -264,16 +264,21 @@ class Solution {
## Python
```python
//递归
class Solution:
"""二叉树的最近公共祖先 递归法"""
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root or root == p or root == q: return root //找到了节点p或者q或者遇到空节点
left = self.lowestCommonAncestor(root.left,p,q) //
right = self.lowestCommonAncestor(root.right,p,q) //
if left and right: return root //: left和right不为空root就是最近公共节点
elif left and not right: return left //目标节点是通过left返回的
elif not left and right: return right //目标节点是通过right返回的
else: return None //没找到
if not root or root == p or root == q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right:
return root
if left:
return left
return right
```
## Go
@ -340,4 +345,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -256,6 +256,25 @@ class Solution {
}
```
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.赎金信
@ -267,4 +286,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -367,28 +367,106 @@ class Solution {
}
}
```
Python
```Python
```java
// 解法2
class Solution {
/**
* 迭代法
*/
public List<String> binaryTreePaths(TreeNode root) {
List<String> result = new ArrayList<>();
if (root == null)
return result;
Stack<Object> stack = new Stack<>();
// 节点和路径同时入栈
stack.push(root);
stack.push(root.val + "");
while (!stack.isEmpty()) {
// 节点和路径同时出栈
String path = (String) stack.pop();
TreeNode node = (TreeNode) stack.pop();
// 若找到叶子节点
if (node.left == null && node.right == null) {
result.add(path);
}
//右子节点不为空
if (node.right != null) {
stack.push(node.right);
stack.push(path + "->" + node.right.val);
}
//左子节点不为空
if (node.left != null) {
stack.push(node.left);
stack.push(path + "->" + node.left.val);
}
}
return result;
}
}
```
---
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 binaryTreePaths(self, root: TreeNode) -> List[str]:
path=[]
res=[]
def backtrace(root, path):
if not root:return
path.append(root.val)
if (not root.left)and (not root.right):
res.append(path[:])
ways=[]
if root.left:ways.append(root.left)
if root.right:ways.append(root.right)
for way in ways:
backtrace(way,path)
path.pop()
backtrace(root,path)
return ["->".join(list(map(str,i))) for i in res]
path = ''
result = []
if not root: return result
self.traversal(root, path, result)
return result
def traversal(self, cur: TreeNode, path: str, result: List[str]) -> None:
path += str(cur.val)
# 若当前节点为leave直接输出
if not cur.left and not cur.right:
result.append(path)
if cur.left:
# + '->' 是隐藏回溯
self.traversal(cur.left, path + '->', result)
if cur.right:
self.traversal(cur.right, path + '->', result)
```
迭代法:
```python3
from collections import deque
class Solution:
"""二叉树的所有路径 迭代法"""
def binaryTreePaths(self, root: TreeNode) -> List[str]:
# 题目中节点数至少为1
stack, path_st, result = deque([root]), deque(), []
path_st.append(str(root.val))
while stack:
cur = stack.pop()
path = path_st.pop()
# 如果当前节点为叶子节点,添加路径到结果中
if not (cur.left or cur.right):
result.append(path)
if cur.right:
stack.append(cur.right)
path_st.append(path + '->' + str(cur.right.val))
if cur.left:
stack.append(cur.left)
path_st.append(path + '->' + str(cur.left.val))
return result
```
---
Go
```go
@ -413,7 +491,7 @@ func binaryTreePaths(root *TreeNode) []string {
return res
}
```
---
JavaScript:
1.递归版本
@ -447,4 +525,4 @@ var binaryTreePaths = function(root) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -334,8 +334,8 @@ var numSquares1 = function(n) {
let dp = new Array(n + 1).fill(Infinity)
dp[0] = 0
for(let i = 0; i <= n; i++) {
let val = i * i
for(let i = 1; i**2 <= n; i++) {
let val = i**2
for(let j = val; j <= n; j++) {
dp[j] = Math.min(dp[j], dp[j - val] + 1)
}
@ -361,4 +361,4 @@ var numSquares2 = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -1,4 +1,3 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
@ -6,6 +5,7 @@
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 动态规划:一样的套路,再求一次完全平方数
# 283. 移动零
@ -95,10 +95,25 @@ Python
Go
JavaScript
```javascript
var moveZeroes = function(nums) {
let slow = 0;
for(let fast = 0; fast < nums.length; fast++){
if(nums[fast] != 0){//找到非0的元素
nums[slow] = nums[fast];//把非0的元素赋值给数组慢指针指向的索引处的值
slow++;//慢指针向右移动
}
}
// 后面的元素全变成 0
for(let j = slow; j < nums.length; j++){
nums[j] = 0;
}
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -39,7 +39,7 @@
1. dp[i]的定义
**dp[i]表示i之前包括i的最长上升子序列**
**dp[i]表示i之前包括i的最长上升子序列的长度**
2. 状态转移方程
@ -202,4 +202,4 @@ const lengthOfLIS = (nums) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -27,7 +27,6 @@
## 思路
> 之前我们在[动态规划:最佳买卖股票时机含冷冻期](https://programmercarl.com/0309.最佳买卖股票时机含冷冻期.html)讲过一次这道题目,讲解的过程感觉不是很严谨,和录友们也聊过这个问题,本着对大家负责的态度,有问题的地方我都会及时纠正,所以重新发文讲解一下。
相对于[动态规划122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II动态规划.html),本题加上了一个冷冻期
@ -238,4 +237,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)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -330,4 +330,4 @@ const coinChange = (coins, amount) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -450,4 +450,4 @@ var findItinerary = function(tickets) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -368,6 +368,41 @@ class Solution:
return (val1, val2)
```
Go:
动态规划
```go
func rob(root *TreeNode) int {
res := robTree(root)
return max(res[0], res[1])
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func robTree(cur *TreeNode) []int {
if cur == nil {
return []int{0, 0}
}
// 后序遍历
left := robTree(cur.Left)
right := robTree(cur.Right)
// 考虑去偷当前的屋子
robCur := cur.Val + left[0] + right[0]
// 考虑不去偷当前的屋子
notRobCur := max(left[0], left[1]) + max(right[0], right[1])
// 注意顺序0:不偷1:去偷
return []int{notRobCur, robCur}
}
```
JavaScript
> 动态规划
@ -402,4 +437,4 @@ const rob = root => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -276,4 +276,4 @@ var integerBreak = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -206,6 +206,7 @@ var reverseString = function(s) {
Swift:
```swift
// 双指针 - 元组
func reverseString(_ s: inout [Character]) {
var l = 0
var r = s.count - 1
@ -216,6 +217,7 @@ func reverseString(_ s: inout [Character]) {
r -= 1
}
}
```
C:
@ -228,6 +230,7 @@ void reverseString(char* s, int sSize){
char temp = s[left];
s[left++] = s[right];
s[right--] = temp;
}
}
```
@ -237,4 +240,4 @@ void reverseString(char* s, int sSize){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -189,6 +189,79 @@ class Solution:
Go
```go
//方法一:小顶堆
func topKFrequent(nums []int, k int) []int {
map_num:=map[int]int{}
//记录每个元素出现的次数
for _,item:=range nums{
map_num[item]++
}
h:=&IHeap{}
heap.Init(h)
//所有元素入堆堆的长度为k
for key,value:=range map_num{
heap.Push(h,[2]int{key,value})
if h.Len()>k{
heap.Pop(h)
}
}
res:=make([]int,k)
//按顺序返回堆中的元素
for i:=0;i<k;i++{
res[k-i-1]=heap.Pop(h).([2]int)[0]
}
return res
}
//构建小顶堆
type IHeap [][2]int
func (h IHeap) Len()int {
return len(h)
}
func (h IHeap) Less (i,j int) bool {
return h[i][1]<h[j][1]
}
func (h IHeap) Swap(i,j int) {
h[i],h[j]=h[j],h[i]
}
func (h *IHeap) Push(x interface{}){
*h=append(*h,x.([2]int))
}
func (h *IHeap) Pop() interface{}{
old:=*h
n:=len(old)
x:=old[n-1]
*h=old[0:n-1]
return x
}
//方法二:利用O(logn)排序
func topKFrequent(nums []int, k int) []int {
ans:=[]int{}
map_num:=map[int]int{}
for _,item:=range nums {
map_num[item]++
}
for key,_:=range map_num{
ans=append(ans,key)
}
//核心思想:排序
//可以不用包函数,自己实现快排
sort.Slice(ans,func (a,b int)bool{
return map_num[ans[a]]>map_num[ans[b]]
})
return ans[:k]
}
```
javaScript:
```js
@ -291,4 +364,4 @@ PriorityQueue.prototype.compare = function(index1, index2) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

View File

@ -117,7 +117,7 @@ class Solution {
}
```
Python
Python3
```python
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
@ -238,6 +238,25 @@ class Solution {
}
```
Rust:
```rust
use std::collections::HashSet;
impl Solution {
pub fn intersection(nums1: Vec<i32>, nums2: Vec<i32>) -> Vec<i32> {
let mut resultSet: HashSet<i32> = HashSet::with_capacity(1000);
let nums1Set: HashSet<i32> = nums1.into_iter().collect();
for num in nums2.iter() {
if nums1Set.contains(num) {
resultSet.insert(*num);
}
}
let ret: Vec<i32> = resultSet.into_iter().collect();
ret
}
}
```
## 相关题目
* 350.两个数组的交集 II
@ -247,4 +266,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=500> </img></div>

Some files were not shown because too many files have changed in this diff Show More