diff --git a/README.md b/README.md index 582bbbec..28395d50 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ * 二叉树经典题目 -(补充ing) +(持续补充ing) # 算法模板 @@ -286,10 +286,10 @@ vector> levelOrder(TreeNode* root) { * [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) * [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md) -* [0104.二叉树的最大深度 (迭代法的版本)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0104.二叉树的最大深度.md) +* [0104.二叉树的最大深度 (迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0104.二叉树的最大深度.md) -* 0111.二叉树的最小深度(迭代法的版本) -* 0222.完全二叉树的节点个数(迭代法的版本) +* [0111.二叉树的最小深度(迭代法)]((https://github.com/youngyangyang04/leetcode/blob/master/problems/0111.二叉树的最小深度.md)) +* [0222.完全二叉树的节点个数(迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0222.完全二叉树的节点个数.md) ### 二叉树深度 @@ -299,7 +299,6 @@ int getDepth(TreeNode* node) { return 1 + max(getDepth(node->left), getDepth(node->right)); } ``` -(补充ing) ### 二叉树节点数量 @@ -310,6 +309,7 @@ int countNodes(TreeNode* root) { } ``` +(持续补充ing) # LeetCode 最强题解: diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md index dcc223a6..e3f9fce9 100644 --- a/problems/0111.二叉树的最小深度.md +++ b/problems/0111.二叉树的最小深度.md @@ -4,6 +4,59 @@ https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ ## 思路 +这道题目建议和[0104.二叉树的最大深度](https://github.com/youngyangyang04/leetcode/blob/master/problems/0104.二叉树的最大深度.md)一起来做。 + +来来来,一起递归三部曲: + +* 确定递归函数的参数和返回值 +* 确定终止条件 +* 确定单层递归的逻辑 + + +1. 确定递归函数的参数和返回值 + +参数为要传入的二叉树根节点,返回的是int类型的深度。 + +代码如下: + +``` +int getDepth(TreeNode* node) +``` + +2. 确定终止条件 + +终止条件也是遇到空节点返回0,表示当前节点的高度为0。 + +代码如下: + +``` +if (node == NULL) return 0; +``` + + +2. 确定单层递归的逻辑 + +首先取左右子树的深度,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。 + +反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 就可以了。 + +可以看出:求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。 + +代码如下: + +``` +int leftDepth = getDepth(node->left); +int rightDepth = getDepth(node->right); +if (node->left == NULL && node->right != NULL) {  +    return 1 + rightDepth; +}    +if (node->left != NULL && node->right == NULL) {  +    return 1 + leftDepth; +} +return 1 + min(leftDepth, rightDepth); +``` + +整体递归代码如下: ## C++代码 @@ -31,6 +84,14 @@ public: ``` ### 迭代法 + +可以使用二叉树的广度优先遍历(层序遍历),来做这道题目,其实这就是一道模板题了,二叉树的层序遍历模板在这道题解中[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) + +层序遍历如图所示: + +![最小深度](https://img-blog.csdnimg.cn/20200811194719677.png) + +代码如下:(详细注释) ``` class Solution { public: diff --git a/problems/0144.二叉树的前序遍历.md b/problems/0144.二叉树的前序遍历.md index 62c16691..8704e395 100644 --- a/problems/0144.二叉树的前序遍历.md +++ b/problems/0144.二叉树的前序遍历.md @@ -1,8 +1,7 @@ -## 题目地址 +# 题目地址 https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ -## 思路 - +# 思路 这篇文章,**彻底讲清楚应该如何写递归,并给出了前中后序三种不同的迭代法,然后分析迭代法的代码风格为什么没有统一,最后给出统一的前中后序迭代法的代码,帮大家彻底吃透二叉树的深度优先遍历。** @@ -25,7 +24,7 @@ https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ * 中序遍历(左中右):1 4 2 5 7 6 8 * 后序遍历(左右中):1 2 4 7 8 6 5 -### 递归法 +# 递归法 接下来我们来好好谈一谈递归,为什么很多同学看递归算法都是“一看就会,一写就废”。主要是对递归不成体系,没有方法论,每次写递归算法 ,都是靠玄学来写代码,代码能不能编过都靠运气。 @@ -108,7 +107,7 @@ public: } ``` -### 迭代法 +# 迭代法 实践过的同学,应该会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。而迭代法,貌似需要每一种遍历都要写出不同风格的代码。 @@ -138,7 +137,7 @@ public: 这时会发现貌似使用迭代法写出先序遍历并不难,确实不难,但难却难在,我们再用迭代法写中序遍历的时候,发现套路又不一样了,目前的这个逻辑无法直接应用到中序遍历上。 -#### 前后中遍历迭代法不统一的写法 +## 前后中遍历迭代法不统一的写法 为了解释清楚,我说明一下 刚刚在迭代的过程中,其实我们有两个操作,**一个是处理:将元素放进result数组中,一个是访问:遍历节点。** @@ -207,7 +206,7 @@ public: **重头戏来了,接下来介绍一下统一写法。** -#### 前后中遍历迭代法统一的写法 +## 前后中遍历迭代法统一的写法 我们以中序遍历为例,之前说使用栈的话,**无法同时解决处理过程和访问过程不一致的情况**,那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记,标记就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index 7c95c2bb..f7620b81 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -21,8 +21,9 @@ https://leetcode-cn.com/problems/merge-two-binary-trees/ class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { - if (t1 == NULL) return t2; - if (t2 == NULL) return t1; + if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2 + if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1 + // 修改了t1的数值和结构 t1->val += t2->val; t1->left = mergeTrees(t1->left, t2->left); t1->right = mergeTrees(t1->right, t2->right); @@ -38,6 +39,7 @@ public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { if (t1 == NULL) return t2; if (t2 == NULL) return t1; + // 重新定义新的节点,不修改原有两个树的结构 TreeNode* root = new TreeNode(0); root->val = t1->val + t2->val; root->left = mergeTrees(t1->left, t2->left); diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index f497392d..0ea30f35 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -3,12 +3,84 @@ https://leetcode-cn.com/problems/maximum-binary-tree/ ## 思路 -典型的递归问题 +最大二叉树的构建过程如下: -1. 每一层要返回上一层什么内容 -2. 明确终止条件是什么,明确了返回内容,才知道终止条件要返回什么 -3. 每一层的处理条件 + +典型的递归问题,依然按照递归三部曲来分析: + +* 确定递归函数的参数和返回值 +* 确定终止条件 +* 确定单层递归的逻辑 + +### 确定递归函数的参数和返回值 + +参数就是传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针。 + +代码如下: + +``` +TreeNode* constructMaximumBinaryTree(vector& nums) + +``` +### 确定终止条件 + +题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了具体节点了,那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回。 + +代码如下: + +``` +TreeNode* node = new TreeNode(0); +if (nums.size() == 1) { +    node->val = nums[0]; +    return node; +} +``` +### 确定单层递归的逻辑 + +这里有三步工作 + +1. 先要找到数组中最大的值和对应的下表, 最大的值就是根节点 + +代码如下: +``` +int maxValue = 0; +int maxValueIndex = 0; +for (int i = 0; i < nums.size(); i++) { +    if (nums[i] > maxValue) { +        maxValue = nums[i]; +        maxValueIndex = i; +    } +} +TreeNode* node = new TreeNode(0); +node->val = maxValue; +``` + +2. 最大值所在的下表左区间 构造左子树 + +这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值。 + +代码如下: +``` +if (maxValueIndex > 0) { +    vector newVec(nums.begin(), nums.begin() + maxValueIndex); +    node->left = constructMaximumBinaryTree(newVec); +} +``` + +3. 最大值所在的下表右区间 构造右子树 + +判断maxValueIndex < (nums.size() - 1),确保右区间至少有一个数值。 + +代码如下: + +``` +if (maxValueIndex < (nums.size() - 1)) { +    vector newVec(nums.begin() + maxValueIndex + 1, nums.end()); +    node->right = constructMaximumBinaryTree(newVec); +} +``` +这样我们就分析完了,整体代码如下:(详细注释) ## C++代码 @@ -21,6 +93,7 @@ public: node->val = nums[0]; return node; } + // 找到数组中最大的值和对应的下表 int maxValue = 0; int maxValueIndex = 0; for (int i = 0; i < nums.size(); i++) { @@ -30,10 +103,12 @@ public: } } node->val = maxValue; + // 最大值所在的下表左区间 构造左子树 if (maxValueIndex > 0) { vector newVec(nums.begin(), nums.begin() + maxValueIndex); node->left = constructMaximumBinaryTree(newVec); } + // 最大值所在的下表右区间 构造右子树 if (maxValueIndex < (nums.size() - 1)) { vector newVec(nums.begin() + maxValueIndex + 1, nums.end()); node->right = constructMaximumBinaryTree(newVec); @@ -42,3 +117,4 @@ public: } }; ``` +> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 diff --git a/video/654.最大二叉树.mp4 b/video/654.最大二叉树.mp4 new file mode 100644 index 00000000..70515bfb Binary files /dev/null and b/video/654.最大二叉树.mp4 differ