mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-09 03:34:02 +08:00
Update
This commit is contained in:
@ -302,7 +302,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)
|
1. [关于贪心算法,你该了解这些!](./problems/贪心算法理论基础.md)
|
||||||
2. [贪心算法:分发饼干](./problems/0455.分发饼干.md)
|
2. [贪心算法:分发饼干](./problems/0455.分发饼干.md)
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
如果输入"233"呢,那么就三层for循环,如果"2333"呢,就四层for循环.......
|
如果输入"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。
|
再来看参数,参数指定是有题目中给的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也表示树的深度。
|
这个index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ if (index == digits.size()) {
|
|||||||
|
|
||||||
然后for循环来处理这个字符集,代码如下:
|
然后for循环来处理这个字符集,代码如下:
|
||||||
|
|
||||||
```
|
```CPP
|
||||||
int digit = digits[index] - '0'; // 将index指向的数字转为int
|
int digit = digits[index] - '0'; // 将index指向的数字转为int
|
||||||
string letters = letterMap[digit]; // 取数字对应的字符集
|
string letters = letterMap[digit]; // 取数字对应的字符集
|
||||||
for (int i = 0; i < letters.size(); i++) {
|
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++代码:
|
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://programmercarl.com/回溯算法理论基础.html)中的回溯法模板,不难写出如下C++代码:
|
||||||
|
|
||||||
|
|
||||||
```c++
|
```CPP
|
||||||
// 版本一
|
// 版本一
|
||||||
class Solution {
|
class Solution {
|
||||||
private:
|
private:
|
||||||
@ -183,7 +183,7 @@ public:
|
|||||||
|
|
||||||
一些写法,是把回溯的过程放在递归函数里了,例如如下代码,我可以写成这样:(注意注释中不一样的地方)
|
一些写法,是把回溯的过程放在递归函数里了,例如如下代码,我可以写成这样:(注意注释中不一样的地方)
|
||||||
|
|
||||||
```c++
|
```CPP
|
||||||
// 版本二
|
// 版本二
|
||||||
class Solution {
|
class Solution {
|
||||||
private:
|
private:
|
||||||
@ -236,10 +236,10 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 其他语言版本
|
# 其他语言版本
|
||||||
|
|
||||||
|
|
||||||
Java:
|
## Java
|
||||||
```Java
|
```Java
|
||||||
class Solution {
|
class Solution {
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ class Solution {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Python:
|
## Python
|
||||||
|
|
||||||
```Python
|
```Python
|
||||||
class Solution:
|
class Solution:
|
||||||
@ -340,10 +340,9 @@ class Solution:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Go:
|
## Go
|
||||||
|
|
||||||
|
主要在于递归中传递下一个数字
|
||||||
> 主要在于递归中传递下一个数字
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func letterCombinations(digits string) []string {
|
func letterCombinations(digits string) []string {
|
||||||
@ -382,7 +381,7 @@ func recursion(tempString ,digits string, Index int,digitsMap [10]string, res *[
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
javaScript:
|
## javaScript
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var letterCombinations = function(digits) {
|
var letterCombinations = function(digits) {
|
||||||
|
@ -432,6 +432,70 @@ 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)
|
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||||
|
@ -33,11 +33,11 @@
|
|||||||
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。
|
**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](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++代码
|
## C++代码
|
||||||
|
|
||||||
```
|
```CPP
|
||||||
class Solution {
|
class Solution {
|
||||||
private:
|
private:
|
||||||
vector<vector<int>> result;
|
vector<vector<int>> result;
|
||||||
|
@ -147,7 +147,7 @@ for (int col = 0; col < n; col++) {
|
|||||||
|
|
||||||
代码如下:
|
代码如下:
|
||||||
|
|
||||||
```
|
```CPP
|
||||||
bool isValid(int row, int col, vector<string>& chessboard, int n) {
|
bool isValid(int row, int col, vector<string>& chessboard, int n) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
// 检查列
|
// 检查列
|
||||||
|
@ -103,6 +103,8 @@ public:
|
|||||||
|
|
||||||
当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。
|
当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。
|
||||||
|
|
||||||
|
不少同学认为 如果输入用例都是-1,或者 都是负数,这个贪心算法跑出来的结果是0, 这是**又一次证明脑洞模拟不靠谱的经典案例**,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。
|
||||||
|
|
||||||
## 动态规划
|
## 动态规划
|
||||||
|
|
||||||
当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列,后续到动态规划系列的时候会详细讲解本题的dp方法。
|
当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列,后续到动态规划系列的时候会详细讲解本题的dp方法。
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
也可以直接看我的B站视频:[带你学透回溯算法-组合问题(对应力扣题目:77.组合)](https://www.bilibili.com/video/BV1ti4y1L7cv#reply3733925949)
|
也可以直接看我的B站视频:[带你学透回溯算法-组合问题(对应力扣题目:77.组合)](https://www.bilibili.com/video/BV1ti4y1L7cv#reply3733925949)
|
||||||
|
|
||||||
## 思路
|
# 思路
|
||||||
|
|
||||||
|
|
||||||
本题这是回溯法的经典题目。
|
本题这是回溯法的经典题目。
|
||||||
@ -37,7 +37,7 @@
|
|||||||
直接的解法当然是使用for循环,例如示例中k为2,很容易想到 用两个for循环,这样就可以输出 和示例中一样的结果。
|
直接的解法当然是使用for循环,例如示例中k为2,很容易想到 用两个for循环,这样就可以输出 和示例中一样的结果。
|
||||||
|
|
||||||
代码如下:
|
代码如下:
|
||||||
```
|
```CPP
|
||||||
int n = 4;
|
int n = 4;
|
||||||
for (int i = 1; i <= n; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
for (int j = i + 1; j <= n; j++) {
|
for (int j = i + 1; j <= n; j++) {
|
||||||
@ -49,7 +49,7 @@ for (int i = 1; i <= n; i++) {
|
|||||||
输入:n = 100, k = 3
|
输入:n = 100, k = 3
|
||||||
那么就三层for循环,代码如下:
|
那么就三层for循环,代码如下:
|
||||||
|
|
||||||
```
|
```CPP
|
||||||
int n = 100;
|
int n = 100;
|
||||||
for (int i = 1; i <= n; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
for (int j = i + 1; j <= n; j++) {
|
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 {
|
class Solution {
|
||||||
private:
|
private:
|
||||||
vector<vector<int>> result;
|
vector<vector<int>> result;
|
||||||
@ -336,10 +336,10 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 其他语言版本
|
# 其他语言版本
|
||||||
|
|
||||||
|
|
||||||
Java:
|
## Java:
|
||||||
```java
|
```java
|
||||||
class Solution {
|
class Solution {
|
||||||
List<List<Integer>> result = new ArrayList<>();
|
List<List<Integer>> result = new ArrayList<>();
|
||||||
@ -369,7 +369,25 @@ class Solution {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Python:
|
## 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
|
```python3
|
||||||
class Solution:
|
class Solution:
|
||||||
def combine(self, n: int, k: int) -> List[List[int]]:
|
def combine(self, n: int, k: int) -> List[List[int]]:
|
||||||
@ -379,14 +397,18 @@ class Solution:
|
|||||||
if len(path) == k:
|
if len(path) == k:
|
||||||
res.append(path[:])
|
res.append(path[:])
|
||||||
return
|
return
|
||||||
for i in range(startIndex,n+1):
|
for i in range(startIndex,n-(k-len(path))+2): #优化的地方
|
||||||
path.append(i) #处理节点
|
path.append(i) #处理节点
|
||||||
backtrack(n,k,i+1) #递归
|
backtrack(n,k,i+1) #递归
|
||||||
path.pop() #回溯,撤销处理的节点
|
path.pop() #回溯,撤销处理的节点
|
||||||
backtrack(n,k,1)
|
backtrack(n,k,1)
|
||||||
return res
|
return res
|
||||||
```
|
```
|
||||||
javascript
|
|
||||||
|
|
||||||
|
## javascript
|
||||||
|
|
||||||
|
剪枝:
|
||||||
```javascript
|
```javascript
|
||||||
let result = []
|
let result = []
|
||||||
let path = []
|
let path = []
|
||||||
@ -407,7 +429,37 @@ const combineHelper = (n, k, startIndex) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Go:
|
|
||||||
|
|
||||||
|
|
||||||
|
## Go
|
||||||
|
```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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
剪枝:
|
||||||
```Go
|
```Go
|
||||||
var res [][]int
|
var res [][]int
|
||||||
func combine(n int, k int) [][]int {
|
func combine(n int, k int) [][]int {
|
||||||
@ -435,7 +487,7 @@ func backtrack(n,k,start int,track []int){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
C:
|
## C
|
||||||
```c
|
```c
|
||||||
int* path;
|
int* path;
|
||||||
int pathTop;
|
int pathTop;
|
||||||
@ -489,6 +541,60 @@ 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)
|
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
求子集问题和[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:分割问题!](https://programmercarl.com/0131.分割回文串.html)又不一样了。
|
求子集问题和[77.组合](https://programmercarl.com/0077.组合.html)和[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)又不一样了。
|
||||||
|
|
||||||
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,**那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!**
|
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,**那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!**
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ public:
|
|||||||
|
|
||||||
相信大家经过了
|
相信大家经过了
|
||||||
* 组合问题:
|
* 组合问题:
|
||||||
* [回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)
|
* [回溯算法:求组合问题](https://programmercarl.com/0077.组合.html)
|
||||||
* [回溯算法:组合问题再剪剪枝](https://programmercarl.com/0077.组合优化.html)
|
* [回溯算法:组合问题再剪剪枝](https://programmercarl.com/0077.组合优化.html)
|
||||||
* [回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)
|
* [回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)
|
||||||
* [回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
|
* [回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html)
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
做本题之前一定要先做[78.子集](https://programmercarl.com/0078.子集.html)。
|
做本题之前一定要先做[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)中已经详细讲解过了,和本题是一个套路**。
|
那么关于回溯算法中的去重问题,**在[40.组合总和II](https://programmercarl.com/0040.组合总和II.html)中已经详细讲解过了,和本题是一个套路**。
|
||||||
|
|
||||||
|
@ -45,11 +45,11 @@ 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一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。
|
startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ startIndex一定是需要的,因为不能重复分割,记录下一层递归
|
|||||||
|
|
||||||
* 递归终止条件
|
* 递归终止条件
|
||||||
|
|
||||||
终止条件和[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)情况就不同了,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。
|
终止条件和[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)情况就不同了,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。
|
||||||
|
|
||||||
pointNum表示逗点数量,pointNum为3说明字符串分成了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]这个区间就是截取的子串,需要判断这个子串是否合法。
|
在`for (int i = startIndex; i < s.size(); i++)`循环中 [startIndex, i]这个区间就是截取的子串,需要判断这个子串是否合法。
|
||||||
|
|
||||||
@ -239,11 +239,11 @@ public:
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
在[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)中我列举的分割字符串的难点,本题都覆盖了。
|
在[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)中我列举的分割字符串的难点,本题都覆盖了。
|
||||||
|
|
||||||
而且本题还需要操作字符串添加逗号作为分隔符,并验证区间的合法性。
|
而且本题还需要操作字符串添加逗号作为分隔符,并验证区间的合法性。
|
||||||
|
|
||||||
可以说是[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)的加强版。
|
可以说是[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)的加强版。
|
||||||
|
|
||||||
在本文的树形结构图中,我已经把详细的分析思路都画了出来,相信大家看了之后一定会思路清晰不少!
|
在本文的树形结构图中,我已经把详细的分析思路都画了出来,相信大家看了之后一定会思路清晰不少!
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||||
|
|
||||||
|
# 二叉树层序遍历登场!
|
||||||
|
|
||||||
学会二叉树的层序遍历,可以一口气打完以下十题:
|
学会二叉树的层序遍历,可以一口气打完以下十题:
|
||||||
|
|
||||||
@ -20,7 +21,6 @@
|
|||||||
* 104.二叉树的最大深度
|
* 104.二叉树的最大深度
|
||||||
* 111.二叉树的最小深度
|
* 111.二叉树的最小深度
|
||||||
|
|
||||||
在之前写过这篇文章 [二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html),可惜当时只打了5个,还不够,再给我一次机会,我打十个!
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -131,9 +131,9 @@ public:
|
|||||||
|
|
||||||
一旦想到这里了,很自然就会想到贪心了,即:只收集每天的正利润,最后稳稳的就是最大利润了。
|
一旦想到这里了,很自然就会想到贪心了,即:只收集每天的正利润,最后稳稳的就是最大利润了。
|
||||||
|
|
||||||
## 其他语言版本
|
# 其他语言版本
|
||||||
|
|
||||||
Java:
|
## Java
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// 贪心思路
|
// 贪心思路
|
||||||
@ -171,7 +171,7 @@ class Solution { // 动态规划
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Python:
|
## Python
|
||||||
```python
|
```python
|
||||||
class Solution:
|
class Solution:
|
||||||
def maxProfit(self, prices: List[int]) -> int:
|
def maxProfit(self, prices: List[int]) -> int:
|
||||||
@ -181,7 +181,21 @@ class Solution:
|
|||||||
return result
|
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
|
```golang
|
||||||
//贪心算法
|
//贪心算法
|
||||||
func maxProfit(prices []int) int {
|
func maxProfit(prices []int) int {
|
||||||
@ -217,9 +231,9 @@ func maxProfit(prices []int) int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Javascript:
|
## Javascript
|
||||||
|
贪心
|
||||||
```Javascript
|
```Javascript
|
||||||
// 贪心
|
|
||||||
var maxProfit = function(prices) {
|
var maxProfit = function(prices) {
|
||||||
let result = 0
|
let result = 0
|
||||||
for(let i = 1; i < prices.length; i++) {
|
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
|
```c
|
||||||
int maxProfit(int* prices, int pricesSize){
|
int maxProfit(int* prices, int pricesSize){
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
输出: [[1,2,6], [1,3,5], [2,3,4]]
|
输出: [[1,2,6], [1,3,5], [2,3,4]]
|
||||||
|
|
||||||
|
|
||||||
## 思路
|
# 思路
|
||||||
|
|
||||||
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
|
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ if (sum > targetSum) { // 剪枝操作
|
|||||||
|
|
||||||
最后C++代码如下:
|
最后C++代码如下:
|
||||||
|
|
||||||
```c++
|
```CPP
|
||||||
class Solution {
|
class Solution {
|
||||||
private:
|
private:
|
||||||
vector<vector<int>> result; // 存放结果集
|
vector<vector<int>> result; // 存放结果集
|
||||||
@ -223,10 +223,10 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 其他语言版本
|
# 其他语言版本
|
||||||
|
|
||||||
|
|
||||||
Java:
|
## Java
|
||||||
|
|
||||||
模板方法
|
模板方法
|
||||||
```java
|
```java
|
||||||
@ -299,7 +299,8 @@ class Solution {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Python:
|
## Python
|
||||||
|
|
||||||
```py
|
```py
|
||||||
class Solution:
|
class Solution:
|
||||||
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
|
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
|
||||||
@ -320,10 +321,9 @@ class Solution:
|
|||||||
return res
|
return res
|
||||||
```
|
```
|
||||||
|
|
||||||
Go:
|
## Go:
|
||||||
|
|
||||||
|
回溯+减枝
|
||||||
> 回溯+减枝
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func combinationSum3(k int, n int) [][]int {
|
func combinationSum3(k int, n int) [][]int {
|
||||||
@ -353,7 +353,7 @@ func backTree(n,k,startIndex int,track *[]int,result *[][]int){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
javaScript:
|
## javaScript:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// 等差数列
|
// 等差数列
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
## 思路
|
## 思路
|
||||||
|
|
||||||
> 之前我们在[动态规划:最佳买卖股票时机含冷冻期](https://programmercarl.com/0309.最佳买卖股票时机含冷冻期.html)讲过一次这道题目,讲解的过程感觉不是很严谨,和录友们也聊过这个问题,本着对大家负责的态度,有问题的地方我都会及时纠正,所以重新发文讲解一下。
|
|
||||||
|
|
||||||
相对于[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html),本题加上了一个冷冻期
|
相对于[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html),本题加上了一个冷冻期
|
||||||
|
|
||||||
|
@ -472,7 +472,6 @@ var deleteNode = function (root, key) {
|
|||||||
cur = cur.left;
|
cur = cur.left;
|
||||||
}
|
}
|
||||||
cur.left = root.left;
|
cur.left = root.left;
|
||||||
let temp = root;
|
|
||||||
root = root.right;
|
root = root.right;
|
||||||
delete root;
|
delete root;
|
||||||
return root;
|
return root;
|
||||||
|
@ -33,11 +33,11 @@
|
|||||||
|
|
||||||
这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。
|
这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。
|
||||||
|
|
||||||
这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)。
|
这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的[90.子集II](https://programmercarl.com/0090.子集II.html)。
|
||||||
|
|
||||||
就是因为太像了,更要注意差别所在,要不就掉坑里了!
|
就是因为太像了,更要注意差别所在,要不就掉坑里了!
|
||||||
|
|
||||||
在[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)中我们是通过排序,再加一个标记数组来达到去重的目的。
|
在[90.子集II](https://programmercarl.com/0090.子集II.html)中我们是通过排序,再加一个标记数组来达到去重的目的。
|
||||||
|
|
||||||
而本题求自增子序列,是不能对原数组经行排序的,排完序的数组都是自增子序列了。
|
而本题求自增子序列,是不能对原数组经行排序的,排完序的数组都是自增子序列了。
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
C++代码如下:
|
C++代码如下:
|
||||||
|
|
||||||
```c++
|
```CPP
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
bool validMountainArray(vector<int>& A) {
|
bool validMountainArray(vector<int>& A) {
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
|
|
||||||
拿示例一A = [1,4,2], B = [1,2,4]为例,相交情况如图:
|
拿示例一A = [1,4,2], B = [1,2,4]为例,相交情况如图:
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)
|
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)
|
||||||
|
|
||||||
|
243
problems/前序/ACM模式如何构建二叉树.md
Normal file
243
problems/前序/ACM模式如何构建二叉树.md
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
|
||||||
|
# 力扣上如何自己构造二叉树输入用例?
|
||||||
|
|
||||||
|
经常有录友问,二叉树的题目中输入用例在ACM模式下应该怎么构造呢?
|
||||||
|
|
||||||
|
力扣上的题目,输入用例就给了一个数组,怎么就能构造成二叉树呢?
|
||||||
|
|
||||||
|
这次就给大家好好讲一讲!
|
||||||
|
|
||||||
|
就拿最近公众号上 二叉树的打卡题目来说:
|
||||||
|
|
||||||
|
[538.把二叉搜索树转换为累加树](https://mp.weixin.qq.com/s/rlJUFGCnXsIMX0Lg-fRpIw)
|
||||||
|
|
||||||
|
其输入用例,就是用一个数组来表述 二叉树,如下:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
一直跟着公众号学算法的录友 应该知道,我在[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/Dza-fqjTyGrsRw4PWNKdxA),已经讲过,**只有 中序与后序 和 中序和前序 可以确定一颗唯一的二叉树。 前序和后序是不能确定唯一的二叉树的**。
|
||||||
|
|
||||||
|
那么[538.把二叉搜索树转换为累加树](https://mp.weixin.qq.com/s/rlJUFGCnXsIMX0Lg-fRpIw)的示例中,为什么,一个序列(数组或者是字符串)就可以确定二叉树了呢?
|
||||||
|
|
||||||
|
很明显,是后台直接明确了构造规则。
|
||||||
|
|
||||||
|
再看一下 这个 输入序列 和 对应的二叉树。
|
||||||
|

|
||||||
|
|
||||||
|
从二叉树 推导到 序列,大家可以发现这就是层序遍历。
|
||||||
|
|
||||||
|
但从序列 推导到 二叉树,很多同学就看不懂了,这得怎么转换呢。
|
||||||
|
|
||||||
|
我在 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/q_eKfL8vmSbSFcptZ3aeRA)已经详细讲过,二叉树可以有两种存储方式,一种是 链式存储,另一种是顺序存储。
|
||||||
|
|
||||||
|
链式存储,就是大家熟悉的二叉树,用指针指向左右孩子。
|
||||||
|
|
||||||
|
顺序存储,就是用一个数组来存二叉树,其方式如图所示:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
那么此时大家是不是应该知道了,数组如何转化成 二叉树了。**如果父节点的数组下标是i,那么它的左孩子下标就是i * 2 + 1,右孩子下标就是 i * 2 + 2**。
|
||||||
|
|
||||||
|
那么这里又有同学疑惑了,这些我都懂了,但我还是不知道 应该 怎么构造。
|
||||||
|
|
||||||
|
来,咱上代码。 昨天晚上 速度敲了一遍实现代码。
|
||||||
|
|
||||||
|
具体过程看注释:
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
// 根据数组构造二叉树
|
||||||
|
TreeNode* construct_binary_tree(const vector<int>& vec) {
|
||||||
|
vector<TreeNode*> vecTree (vec.size(), NULL);
|
||||||
|
TreeNode* root = NULL;
|
||||||
|
// 把输入数值数组,先转化为二叉树节点数组
|
||||||
|
for (int i = 0; i < vec.size(); i++) {
|
||||||
|
TreeNode* node = NULL;
|
||||||
|
if (vec[i] != -1) node = new TreeNode(vec[i]); // 用 -1 表示null
|
||||||
|
vecTree[i] = node;
|
||||||
|
if (i == 0) root = node;
|
||||||
|
}
|
||||||
|
// 遍历一遍,根据规则左右孩子赋值就可以了
|
||||||
|
// 注意这里 结束规则是 i * 2 + 2 < vec.size(),避免空指针
|
||||||
|
for (int i = 0; i * 2 + 2 < vec.size(); i++) {
|
||||||
|
if (vecTree[i] != NULL) {
|
||||||
|
// 线性存储转连式存储关键逻辑
|
||||||
|
vecTree[i]->left = vecTree[i * 2 + 1];
|
||||||
|
vecTree[i]->right = vecTree[i * 2 + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这个函数最后返回的 指针就是 根节点的指针, 这就是 传入二叉树的格式了,也就是 力扣上的用例输入格式,如图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
也有不少同学在做ACM模式的题目,就经常疑惑:
|
||||||
|
|
||||||
|
* 让我传入数值,我会!
|
||||||
|
* 让我传入数组,我会!
|
||||||
|
* 让我传入链表,我也会!
|
||||||
|
* **让我传入二叉树,我懵了,啥? 传入二叉树?二叉树怎么传?**
|
||||||
|
|
||||||
|
其实传入二叉树,就是传入二叉树的根节点的指针,和传入链表都是一个逻辑。
|
||||||
|
|
||||||
|
这种现象主要就是大家对ACM模式过于陌生,说实话,ACM模式才真正的考察代码能力(注意不是算法能力),而 力扣的核心代码模式 总有一种 不够彻底的感觉。
|
||||||
|
|
||||||
|
所以,如果大家对ACM模式不够了解,一定要多去练习!
|
||||||
|
|
||||||
|
那么以上的代码,我们根据数组构造二叉树,接来下我们在 把 这个二叉树打印出来,看看是不是 我们输入的二叉树结构,这里就用到了层序遍历,我们在[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)中讲过。
|
||||||
|
|
||||||
|
|
||||||
|
完整测试代码如下:
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
struct TreeNode {
|
||||||
|
int val;
|
||||||
|
TreeNode *left;
|
||||||
|
TreeNode *right;
|
||||||
|
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据数组构造二叉树
|
||||||
|
TreeNode* construct_binary_tree(const vector<int>& vec) {
|
||||||
|
vector<TreeNode*> vecTree (vec.size(), NULL);
|
||||||
|
TreeNode* root = NULL;
|
||||||
|
for (int i = 0; i < vec.size(); i++) {
|
||||||
|
TreeNode* node = NULL;
|
||||||
|
if (vec[i] != -1) node = new TreeNode(vec[i]);
|
||||||
|
vecTree[i] = node;
|
||||||
|
if (i == 0) root = node;
|
||||||
|
}
|
||||||
|
for (int i = 0; i * 2 + 2 < vec.size(); i++) {
|
||||||
|
if (vecTree[i] != NULL) {
|
||||||
|
vecTree[i]->left = vecTree[i * 2 + 1];
|
||||||
|
vecTree[i]->right = vecTree[i * 2 + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 层序打印打印二叉树
|
||||||
|
void print_binary_tree(TreeNode* root) {
|
||||||
|
queue<TreeNode*> que;
|
||||||
|
if (root != NULL) que.push(root);
|
||||||
|
vector<vector<int>> result;
|
||||||
|
while (!que.empty()) {
|
||||||
|
int size = que.size();
|
||||||
|
vector<int> vec;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
TreeNode* node = que.front();
|
||||||
|
que.pop();
|
||||||
|
if (node != NULL) {
|
||||||
|
vec.push_back(node->val);
|
||||||
|
que.push(node->left);
|
||||||
|
que.push(node->right);
|
||||||
|
}
|
||||||
|
// 这里的处理逻辑是为了把null节点打印出来,用-1 表示null
|
||||||
|
else vec.push_back(-1);
|
||||||
|
}
|
||||||
|
result.push_back(vec);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
for (int j = 0; j < result[i].size(); j++) {
|
||||||
|
cout << result[i][j] << " ";
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 注意本代码没有考虑输入异常数据的情况
|
||||||
|
// 用 -1 来表示null
|
||||||
|
vector<int> vec = {4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8};
|
||||||
|
TreeNode* root = construct_binary_tree(vec);
|
||||||
|
print_binary_tree(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
可以看出我们传入的数组是:{4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8} , 这里是用 -1 来表示null,
|
||||||
|
|
||||||
|
和 [538.把二叉搜索树转换为累加树](https://mp.weixin.qq.com/s/rlJUFGCnXsIMX0Lg-fRpIw) 中的输入是一样的
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
这里可能又有同学疑惑,你这不一样啊,题目是null,你为啥用-1。
|
||||||
|
|
||||||
|
用-1 表示null为了方便举例,如果非要和 力扣输入一样一样的,就是简单的字符串处理,把null 替换为 -1 就行了。
|
||||||
|
|
||||||
|
在来看,测试代码输出的效果:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可以看出和 题目中输入用例 这个图 是一样一样的。 只不过题目中图没有把 空节点 画出来而已。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
大家可以拿我的代码去测试一下,跑一跑。
|
||||||
|
|
||||||
|
**注意:我的测试代码,并没有处理输入异常的情况(例如输入空数组之类的),处理各种输入异常,大家可以自己去练练**。
|
||||||
|
|
||||||
|
|
||||||
|
# 总结
|
||||||
|
|
||||||
|
大家可以发现,这个问题,其实涉及很多知识点,而这些知识点 其实我在文章里都讲过,而且是详细的讲过,如果大家能把这些知识点串起来,很容易解决心中的疑惑了。
|
||||||
|
|
||||||
|
但为什么很多录友都没有想到这个程度呢。
|
||||||
|
|
||||||
|
这也是我反复强调,**代码随想录上的 题目和理论基础,至少要详细刷两遍**。
|
||||||
|
|
||||||
|
**[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)**里有的录友已经开始三刷:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
只做过一遍,真的就是懂了一点皮毛, 第二遍刷才有真的对各个题目有较为深入的理解,也会明白 我为什么要这样安排刷题的顺序了。
|
||||||
|
|
||||||
|
**都是卡哥的良苦用心呀!**
|
||||||
|
|
||||||
|
|
||||||
|
# 其他语言版本
|
||||||
|
|
||||||
|
|
||||||
|
## Java
|
||||||
|
|
||||||
|
```Java
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
```Python
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Go
|
||||||
|
|
||||||
|
```Go
|
||||||
|
```
|
||||||
|
|
||||||
|
## JavaScript
|
||||||
|
|
||||||
|
```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>
|
@ -247,7 +247,7 @@ int binary_search( int arr[], int l, int r, int x) {
|
|||||||
|
|
||||||
每次递归的空间复杂度可以看出主要就是参数里传入的这个arr数组,但需要注意的是在C/C++中函数传递数组参数,不是整个数组拷贝一份传入函数而是传入的数组首元素地址。
|
每次递归的空间复杂度可以看出主要就是参数里传入的这个arr数组,但需要注意的是在C/C++中函数传递数组参数,不是整个数组拷贝一份传入函数而是传入的数组首元素地址。
|
||||||
|
|
||||||
**也就是说每一层递归都是公用一块数组地址空间的**,所以 每次递归的时间复杂度是常数即:O(1)。
|
**也就是说每一层递归都是公用一块数组地址空间的**,所以 每次递归的空间复杂度是常数即:O(1)。
|
||||||
|
|
||||||
再来看递归的深度,二分查找的递归深度是logn ,递归深度就是调用栈的长度,那么这段代码的空间复杂度为 1 * logn = O(logn)。
|
再来看递归的深度,二分查找的递归深度是logn ,递归深度就是调用栈的长度,那么这段代码的空间复杂度为 1 * logn = O(logn)。
|
||||||
|
|
||||||
|
32
problems/知识星球精选/合适自己的就是最好的.md
Normal file
32
problems/知识星球精选/合适自己的就是最好的.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# 合适自己的,才是最好的!
|
||||||
|
|
||||||
|
秋招已经进入下半场了,不少同学也拿到了offer,但不是说非要进大厂,每个人情况都不一样,**合适自己的,就是最好的!**。
|
||||||
|
|
||||||
|
知识星球里有一位录友,就终于拿到了合适自己的offer,并不是大厂,是南京的一家公司,**但很合适自己,其实就非常值得开心**。
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
其实我算是一路见证了这位录友披荆斩棘,**从一开始基础并不好,还是非科班,到 实验室各种不顺利,再到最后面试次次受打击,最后终于拿到自己满意的offer**。
|
||||||
|
|
||||||
|
这一路下来确实不容易!
|
||||||
|
|
||||||
|
这位录友是从几年五月份加入星球。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
然后就开始每天坚持打卡。 可以看看她每天的打卡内容。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
后面因为天天面试,不能坚持打卡了,也是和大部分同学一样,焦虑并努力着。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
星球里完整的记录了 这位录友 从五月份以来每天的学习内容和学习状态,能感觉出来 **虽然苦难重重,但依然元气满满!**
|
||||||
|
|
||||||
|
我在发文的时候 看了一遍她这几个月完整的打卡过程,还是深有感触的。
|
||||||
|
|
||||||
|
星球里还有很多很多这样的录友在每日奋斗着,**我相信 等大家拿到offer之后,在回头看一下当初星球里曾经每日打卡的点点滴滴,不仅会感动自己 也会感动每一位见证者**。
|
||||||
|
|
Reference in New Issue
Block a user