diff --git a/problems/0104.二叉树的最大深度.md b/problems/0104.二叉树的最大深度.md index ee7bd50e..03322c9a 100644 --- a/problems/0104.二叉树的最大深度.md +++ b/problems/0104.二叉树的最大深度.md @@ -59,7 +59,7 @@ int getdepth(treenode* node) if (node == NULL) return 0; ``` -3. 确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。 +3. 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。 代码如下: @@ -591,15 +591,15 @@ var maxdepth = function(root) { var maxdepth = function(root) { //使用递归的方法 递归三部曲 //1. 确定递归函数的参数和返回值 - const getdepth=function(node){ + const getdepth = function(node) { //2. 确定终止条件 - if(node===null){ + if(node === null) { return 0; } //3. 确定单层逻辑 - let leftdepth=getdepth(node.left); - let rightdepth=getdepth(node.right); - let depth=1+Math.max(leftdepth,rightdepth); + let leftdepth = getdepth(node.left); + let rightdepth = getdepth(node.right); + let depth = 1 + Math.max(leftdepth, rightdepth); return depth; } return getdepth(root); diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index 9751f2dd..492e25d9 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -158,7 +158,7 @@ if (node == NULL) { 如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。 -分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉平衡树了。 +分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。 代码如下: @@ -342,7 +342,7 @@ public: **例如:都知道回溯法其实就是递归,但是很少人用迭代的方式去实现回溯算法!** -因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。 +因为对于回溯算法已经是非常复杂的递归了,如果再用迭代的话,就是自己给自己找麻烦,效率也并不一定高。 ## 总结 @@ -559,37 +559,32 @@ class Solution: ### Go ```Go func isBalanced(root *TreeNode) bool { - if root==nil{ - return true - } - if !isBalanced(root.Left) || !isBalanced(root.Right){ - return false - } - LeftH:=maxdepth(root.Left)+1 - RightH:=maxdepth(root.Right)+1 - if abs(LeftH-RightH)>1{ + h := getHeight(root) + if h == -1 { return false } return true } -func maxdepth(root *TreeNode)int{ - if root==nil{ +// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1 +func getHeight(root *TreeNode) int { + if root == nil { return 0 } - return max(maxdepth(root.Left),maxdepth(root.Right))+1 + l, r := getHeight(root.Left), getHeight(root.Right) + if l == -1 || r == -1 { + return -1 + } + if l - r > 1 || r - l > 1 { + return -1 + } + return max(l, r) + 1 } -func max(a,b int)int{ - if a>b{ +func max(a, b int) int { + if a > b { return a } return b } -func abs(a int)int{ - if a<0{ - return -a - } - return a -} ``` ### JavaScript diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md index 0b2ec0f6..cbe7b7ec 100644 --- a/problems/0111.二叉树的最小深度.md +++ b/problems/0111.二叉树的最小深度.md @@ -39,7 +39,7 @@ * 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始) * 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始) -那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,这不过这个最小距离 也同样是最小深度。 +那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。 以下讲解中遍历顺序上依然采用后序遍历(因为要比较递归返回之后的结果,本文我也给出前序遍历的写法)。 @@ -199,7 +199,7 @@ public: 如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html) -**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点** +**需要注意的是,只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点** 代码如下:(详细注释) diff --git a/problems/0222.完全二叉树的节点个数.md b/problems/0222.完全二叉树的节点个数.md index 8a3a15f3..b87877c9 100644 --- a/problems/0222.完全二叉树的节点个数.md +++ b/problems/0222.完全二叉树的节点个数.md @@ -63,7 +63,7 @@ int getNodesNum(TreeNode* cur) { if (cur == NULL) return 0; ``` -3. 确定单层递归的逻辑:先求它的左子树的节点数量,再求的右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。 +3. 确定单层递归的逻辑:先求它的左子树的节点数量,再求右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。 代码如下: @@ -168,7 +168,7 @@ public: 可以看出如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。 -这里关键在于如果去判断一个左子树或者右子树是不是满二叉树呢? +这里关键在于如何去判断一个左子树或者右子树是不是满二叉树呢? 在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。如图: @@ -178,13 +178,13 @@ public: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220829163709.png) -哪有录友说了,这种情况,递归向左遍历的深度等于递归向右遍历的深度,但也不是满二叉树,如题: +那有录友说了,这种情况,递归向左遍历的深度等于递归向右遍历的深度,但也不是满二叉树,如题: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220829163811.png) 如果这么想,大家就是对 完全二叉树理解有误区了,**以上这棵二叉树,它根本就不是一个完全二叉树**! -判断其子树岂不是满二叉树,如果是则利用用公式计算这个子树(满二叉树)的节点数量,如果不是则继续递归,那么 在递归三部曲中,第二部:终止条件的写法应该是这样的: +判断其子树是不是满二叉树,如果是则利用公式计算这个子树(满二叉树)的节点数量,如果不是则继续递归,那么 在递归三部曲中,第二部:终止条件的写法应该是这样的: ```CPP if (root == nullptr) return 0; @@ -292,26 +292,22 @@ class Solution { * 满二叉树的结点数为:2^depth - 1 */ public int countNodes(TreeNode root) { - if(root == null) { - return 0; + if (root == null) return 0; + TreeNode left = root.left; + TreeNode right = root.right; + int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便 + while (left != null) { // 求左子树深度 + left = left.left; + leftDepth++; } - int leftDepth = getDepth(root.left); - int rightDepth = getDepth(root.right); - if (leftDepth == rightDepth) {// 左子树是满二叉树 - // 2^leftDepth其实是 (2^leftDepth - 1) + 1 ,左子树 + 根结点 - return (1 << leftDepth) + countNodes(root.right); - } else {// 右子树是满二叉树 - return (1 << rightDepth) + countNodes(root.left); + while (right != null) { // 求右子树深度 + right = right.right; + rightDepth++; } - } - - private int getDepth(TreeNode root) { - int depth = 0; - while (root != null) { - root = root.left; - depth++; + if (leftDepth == rightDepth) { + return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0 } - return depth; + return countNodes(root.left) + countNodes(root.right) + 1; } } ``` @@ -397,7 +393,7 @@ class Solution: * Right *TreeNode * } */ -//本题直接就是求有多少个节点,无脑存进数组算长度就行了。 +//本题直接就是求有多少个节点,无脑存进结果变量就行了。 func countNodes(root *TreeNode) int { if root == nil { return 0 @@ -473,15 +469,15 @@ func countNodes(root *TreeNode) int { var countNodes = function(root) { //递归法计算二叉树节点数 // 1. 确定递归函数参数 - const getNodeSum=function(node){ + const getNodeSum = function(node) { //2. 确定终止条件 - if(node===null){ + if(node === null) { return 0; } //3. 确定单层递归逻辑 - let leftNum=getNodeSum(node.left); - let rightNum=getNodeSum(node.right); - return leftNum+rightNum+1; + let leftNum = getNodeSum(node.left); + let rightNum = getNodeSum(node.right); + return leftNum + rightNum + 1; } return getNodeSum(root); }; @@ -491,19 +487,19 @@ var countNodes = function(root) { ```javascript var countNodes = function(root) { //层序遍历 - let queue=[]; - if(root===null){ + let queue = []; + if(root === null) { return 0; } queue.push(root); - let nodeNums=0; - while(queue.length){ - let length=queue.length; - while(length--){ - let node=queue.shift(); + let nodeNums = 0; + while(queue.length) { + let length = queue.length; + while(length--) { + let node = queue.shift(); nodeNums++; - node.left&&queue.push(node.left); - node.right&&queue.push(node.right); + node.left && queue.push(node.left); + node.right && queue.push(node.right); } } return nodeNums; @@ -514,24 +510,24 @@ var countNodes = function(root) { ```javascript var countNodes = function(root) { //利用完全二叉树的特点 - if(root===null){ + if(root === null) { return 0; } - let left=root.left; - let right=root.right; - let leftDepth=0,rightDepth=0; - while(left){ - left=left.left; + let left = root.left; + let right = root.right; + let leftDepth = 0, rightDepth = 0; + while(left) { + left = left.left; leftDepth++; } - while(right){ - right=right.right; + while(right) { + right = right.right; rightDepth++; } - if(leftDepth==rightDepth){ - return Math.pow(2,leftDepth+1)-1; + if(leftDepth == rightDepth) { + return Math.pow(2, leftDepth+1) - 1; } - return countNodes(root.left)+countNodes(root.right)+1; + return countNodes(root.left) + countNodes(root.right) + 1; }; ``` diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index a6e14238..2d796671 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -24,7 +24,7 @@ 这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。 -在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。 +在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。 前序遍历以及回溯的过程如图: @@ -44,7 +44,7 @@ void traversal(TreeNode* cur, vector& path, vector& result) 2. 确定递归终止条件 -再写递归的时候都习惯了这么写: +在写递归的时候都习惯了这么写: ``` if (cur == NULL) { @@ -67,7 +67,7 @@ if (cur->left == NULL && cur->right == NULL) { 再来看一下终止处理的逻辑。 -这里使用vector 结构path来记录路径,所以要把vector 结构的path转为string格式,在把这个string 放进 result里。 +这里使用vector 结构path来记录路径,所以要把vector 结构的path转为string格式,再把这个string 放进 result里。 **那么为什么使用了vector 结构来记录路径呢?** 因为在下面处理单层递归逻辑的时候,要做回溯,使用vector方便来做回溯。 @@ -123,7 +123,7 @@ if (cur->right) { path.pop_back(); ``` -这个回溯就要很大的问题,我们知道,**回溯和递归是一一对应的,有一个递归,就要有一个回溯**,这么写的话相当于把递归和回溯拆开了, 一个在花括号里,一个在花括号外。 +这个回溯就有很大的问题,我们知道,**回溯和递归是一一对应的,有一个递归,就要有一个回溯**,这么写的话相当于把递归和回溯拆开了, 一个在花括号里,一个在花括号外。 **所以回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!** @@ -300,16 +300,16 @@ public: ``` -**大家应该可以感受出来,如果把 `path + "->"`作为函数参数就是可以的,因为并有没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)** +**大家应该可以感受出来,如果把 `path + "->"`作为函数参数就是可以的,因为并没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)** -**综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现了出来了。** +**综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现出来了。** ## 拓展 这里讲解本题解的写法逻辑以及一些更具体的细节,下面的讲解中,涉及到C++语法特性,如果不是C++的录友,就可以不看了,避免越看越晕。 -如果是C++的录友,建议本题独立刷过两遍,在看下面的讲解,同样避免越看越晕,造成不必要的负担。 +如果是C++的录友,建议本题独立刷过两遍,再看下面的讲解,同样避免越看越晕,造成不必要的负担。 在第二版本的代码中,其实仅仅是回溯了 `->` 部分(调用两次pop_back,一个pop`>` 一次pop`-`),大家应该疑惑那么 `path += to_string(cur->val);` 这一步为什么没有回溯呢? 一条路径能持续加节点 不做回溯吗? @@ -378,7 +378,7 @@ public: 最后我依然给出了迭代法。 -对于本地充分了解递归与回溯的过程之后,有精力的同学可以在去实现迭代法。 +对于本题充分了解递归与回溯的过程之后,有精力的同学可以再去实现迭代法。 @@ -386,7 +386,7 @@ public: # 其他语言版本 -Java: +## Java: ```Java //解法一 @@ -466,7 +466,7 @@ class Solution { } ``` --- -Python: +## Python: 递归法+隐形回溯 ```Python3 # Definition for a binary tree node. @@ -529,7 +529,7 @@ class Solution: --- -Go: +## Go: 递归法: @@ -591,28 +591,28 @@ func binaryTreePaths(root *TreeNode) []string { ``` --- -JavaScript: +## JavaScript: 递归法: ```javascript var binaryTreePaths = function(root) { //递归遍历+递归三部曲 - let res=[]; + let res = []; //1. 确定递归函数 函数参数 - const getPath=function(node,curPath){ + const getPath = function(node,curPath) { //2. 确定终止条件,到叶子节点就终止 - if(node.left===null&&node.right===null){ - curPath+=node.val; + if(node.left === null && node.right === null) { + curPath += node.val; res.push(curPath); - return ; + return; } //3. 确定单层递归逻辑 - curPath+=node.val+'->'; - node.left&&getPath(node.left,curPath); - node.right&&getPath(node.right,curPath); + curPath += node.val + '->'; + node.left && getPath(node.left, curPath); + node.right && getPath(node.right, curPath); } - getPath(root,''); + getPath(root, ''); return res; }; ``` @@ -644,7 +644,7 @@ var binaryTreePaths = function(root) { }; ``` -TypeScript: +## TypeScript: > 递归法 @@ -698,7 +698,7 @@ function binaryTreePaths(root: TreeNode | null): string[] { }; ``` -Swift: +## Swift: > 递归/回溯 ```swift @@ -765,7 +765,7 @@ func binaryTreePaths(_ root: TreeNode?) -> [String] { } ``` -Scala: +## Scala: 递归: ```scala