二叉树和多叉树是一种天然的递归结构,每一个节点下的左子树或右子树(针对二叉树)都具有和上一个节点代表的树有从属关系,所以在二叉树和多叉树中有许多问题通过递归来解决会十分地简单和方便,本篇文章将对此类题型做一些刷题总结。若对二叉树的基础知识以及递归的基础知识不甚了解的朋友可先去了解这些基础知识后再来查看,本篇不在赘述这些基础知识。
一、递归
首先说说我对于递归的理解:我认为递归是计算机科学高速发展带来的产物,它通过不断地调用自身递归函数解决了有一定规律的问题,将大问题化为小问题,将小问题做出解答,最后再有递归调用栈的层次依次返回已解决的问题,对于递归来说有以下过程:
- 1、递归终止条件
- 2、递归过程(递推)
- 3、递归结果
其中,递归终止条件是每个递归函数都必须存在的,它是整个递归函数的出口,也是一种已知的、最基本的、可直接得出解答的一个case;递归过程也别称为递推,在递归过程中不断地对自身进行递归调用,这个过程中是完成问题由大到小的一个过程;递归结果指的是在每次递归中会将解决的问题返回,最后一次就已经是整个大问题的解决结果了。
最后还有一个很关键的点是,不要尝试去完全理解一个复杂的递归过程:对于递归的理解在于放弃!
二、LeetCode相关题目
2.1、LeetCode 226. 翻转二叉树
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
备注:
这个问题是受到 Max Howell 的 原问题 启发的 :
谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/invert-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
对于这道题来说,用递归来做会十分地简单。先确定一下必须的一些条件:
1、递归终止条件:当当前递归的节点为null时,返回null
2、递归过程:分别对左右两颗子树进行翻转的递归操作,在这里还需要交换当前节点的左子和右子节点
3、递归结果:当前左子和右子被翻转过后的节点
代码实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
//递归终止条件
if (root == null) return null;
//递归过程
//交换子节点
swap(root, root.left, root.right);
//翻转左子树
invertTree(root.left);
//翻转右子树
invertTree(root.right);
return root;
}
private void swap(TreeNode root, TreeNode left, TreeNode right) {
TreeNode temp = left;
root.left = root.right;
root.right = temp;
}
}
2.2、LeetCode 104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个问题还是一样的,要找到根节点为头的二叉树的最大深度,其实就是分别在左子树中找到最大深度+1(包括当前节点)和在右子树中找到最大深度+1,然后比较哪个节点值大一些,大者即为二叉树的最大深度。现在确定以下递归的条件:
- 1、递归终止条件:当遇到子节点时即终止,返回1(代表当前节点数)
- 2、递归过程:当前节点的最大深度即为左子树最大深度+1和右子树最大深度+1中的最大值
- 3、递归结果:返回当前节点代表的子树的节点最大值
代码实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
//节点为空的情况,直接返回0
if (root == null) return 0;
//终止条件
//是叶子节点,返回1
if (root.left == null && root.right == null) return 1;
//递归过程
//分别在左子树和右子树中找最大深度
int leftMaxDepth = maxDepth(root.left);
int rightMawDepth = maxDepth(root.right);
//取左子树和右子树中的最大者+1即为当前节点最大深度
return leftMaxDepth > rightMawDepth ? leftMaxDepth + 1 : rightMawDepth + 1;
}
}
2.3、LeetCode 112. 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题的解决思路是,递归地在当前节点的左右子树中判断该子树的总和值是否为sum - cur.val
,直至找到某条路径的叶子节点时该值是否等于叶子节点的值
-
1、递归终止条件:当遇到子节点时,判断传过来的值是否为该子节点的值
-
2、递归过程:分别在左右子树中寻找,并且每次指行递归函数时都将sum减去当前节点的值
-
3、递归结果:返回当前路径是否满足路径之和
代码实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
//一开始就为空节点
if (root == null) return false;
//递归终止条件:叶子节点的值是否等于层层删减的sum
if (root.left == null && root.right == null) {
return root.val == sum;
}
//递归过程
//在左子树中寻找
if (hasPathSum(root.left, sum - root.val)) {
//左子树中存在该路径
return true;
}
//左子树中不存在该路径,在右子树中寻找
if (hasPathSum(root.right, sum - root.val)) {
//右子树中存在该路径
return true;
}
return false;
}
}
2.4、LeetCode 111. 二叉树的最小深度
这个问题是上面的哪个二叉树的最大深度问题类似,不过不同的是这个问题需要准确地将递归导向到有叶子节点的那颗子树上去,不然会出现解答错误:
- 1、递归终止条件:当前节点为空时返回空;当遇到当前节点不存在左子数时,需要将递归导向到右子树中;当遇到当前节点不存在右子树时,需要将递归导向到左子树中
- 2、递归过程:分别在左右子树中寻找最小值节点总数
- 3、递归结果:当前节点代表的二叉树中的路径节点最小值
代码实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if (root == null) return 0;
//递归终止条件
if (root.left == null && root.right != null){
//左子树为空,右子树不为空,所以要在右子树中寻找叶子节点
return minDepth(root.right) + 1;
} else if (root.right == null && root.left != null) {
//右子树为空,左子树不为空,所以在左子树中寻找叶子节点
return minDepth(root.left) + 1;
}
//递归过程
//左右子树都不为空,分别在左右子树中寻找
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
}
}
2.5 LeetCode 617.合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
注意: 合并必须从两个树的根节点开始。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-binary-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这题使用递归来做十分地简单方便:
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
// TODO: 19-8-23 递归做法
if (t1 == null && t2 == null) return null;
if (t1 == null || t2 == null) {
if (t1 == null) return t2;
if (t2 == null) return t1;
}
TreeNode res = new TreeNode(t1.val + t2.val);
//合并两棵树的左子树
res.left = mergeTrees(t1.left, t2.left);
//合并两棵树的右子树
res.right = mergeTrees(t1.right, t2.right);
return res;
}
未完持续...