递归类型
按照编程技巧分啪
一、将复杂问题分解成两个子问题
1、平衡二叉树(LeetCode题库110题)
自上而下:算每个节点的平衡因子(即左右子树的高度差),判断是否满足条件。
可以分成两个子问题:求树的高度,和遍历树判断每个节点的是否满足条件
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null) {
return true;
}else {
//判断根结点是否满足平衡因子
int ldepth = depth(root.left);
int rdepth = depth(root.right);
System.out.println(ldepth + " " + rdepth);
if(Math.abs(ldepth - rdepth) > 1) {
return false;
}
//判断左子树是否满足平衡因子
if(!isBalanced(root.left)) {
return false;
}
//判断右子树是否满足平衡因子
if(!isBalanced(root.right)) {
return false;
}
return true;
}
}
private int depth(TreeNode root) {
int l = 0;
int r = 0;
int max = 0;
if(root == null) {
return 0;
}else {
l = depth(root.left);
r = depth(root.right);
max = Math.max(l,r);
return (max + 1);
}
}
}
自下而上:在求深度的过程中,只要有一个子树不满足条件,就一路返回-1

class Solution {
public boolean isBalanced(TreeNode root) {
return depth(root) == -1 ? false : true;
}
private int depth(TreeNode root) {
if(root == null){
return 0;
}else{
int l = depth(root.left);
if(l==-1) return -1;
int r = depth(root.right);
if(r == -1) return -1;
if(Math.abs(l-r) > 1) {
return -1;
}
return Math.max(l,r) + 1;
}
}
}
2、二叉树的直径(LeetCode题库543题)
注意此题是有题眼的,一般题干说树的XX,都是在求子树XX的最大值
所以此题就是再求过某个节点的最长路径,只要把每个节点的最长路径求出来,取最大即可;
最大路径一定产生在左右子树的两边,所以要求树的高度。
这样就转换成了两个问题,求树的高度,以及求过每个节点的最长路径

class Solution {
public int diameterOfBinaryTree(TreeNode root) {
if(root == null) {
return 0;
}else {
int l = depth(root.left);
int r = depth(root.right);
int d = l + r;
int dl = diameterOfBinaryTree(root.left);
int dr = diameterOfBinaryTree(root.right);
return Math.max(Math.max(d,dl),Math.max(dl,dr));
}
}
private int depth(TreeNode root) {
int l = 0;
int r = 0;
int max = 0;
if(root == null) {
return 0;
}else {
l = depth(root.left);
r = depth(root.right);
max = Math.max(l,r);
return (max + 1);
}
}
}
3、路径总和III(LeetCode题库437题)
此题的思路也是将复杂的问题分解:我们可以会求一个特定的开始点到任意结束点的符合条件的路径吧,再把树中的每个点遍历一遍,不就是相当于任意起点了吗

class Solution {
public int pathSum(TreeNode root, int sum) {
if(root == null) {
return 0;
}else {
int count = getPathFromRoot(root,sum);
count = count + pathSum(root.left,sum);
count = count + pathSum(root.right,sum);
return count;
}
}
//求从根节点开始到任意节点结束的合适路径
private int getPathFromRoot(TreeNode root, int sum) {
if(root == null) {
return 0;
}else {
int count = 0;
sum = sum - root.val;
if(sum == 0 ) {
count++;
}
count = count + getPathFromRoot(root.left,sum);
count = count + getPathFromRoot(root.right,sum);
return count;
}
}
}
4、树的子树(LeetCode题库572题)
思路分解:先写出判断两颗树是否相等的代码,在遍历一遍树,看有没有符合条件的子树

class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s == null) {
return false;
}else {
if(isEquals(s,t)) {
return true;
}
return isSubtree(s.left,t) || isSubtree(s.right,t);
}
}
private boolean isEquals(TreeNode s, TreeNode t) {
if(s == null || t == null) {
if(s == null && t == null) {
return true;
}else {
return false;
}
}else {
if(s.val != t.val) {
return false;
}else {
return isEquals(s.left,t.left) && isEquals(s.right,t.right);
}
}
}
}
二、"||" 与 "&&"
这一类题,是说左右子树有一个满足就返回true,或是都满足才返回true
1、路径总和(LeetCode题库112题)
class Solution {
//算出每个节点剩余sum值
public boolean hasPathSum(TreeNode root, int sum) {
if(root == null) {
return false;
}else {
sum = sum - root.val;
//相当于在前序遍历中找到一个叶子节点且剩余值为0的
if(root.left == null && root.right == null && sum == 0) {
return true;
}
return hasPathSum(root.left,sum) || hasPathSum(root.right,sum);
}
}
//注意在返回值是bool型的递归函数中不可以随便定义int类型数据
}
2、树的子树(572题)同上面
三、条件控制
这类题在条件的控制上面有一点难度。
1、合并二叉树(LeetCode题库617题)
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1 == null || t2 == null) {
return t1 == null ? t2 : t1;
}else {
t1.val = t1.val + t2.val;
t1.left = mergeTrees(t1.left,t2.left);
t1.right = mergeTrees(t1.right,t2.right);
return t1;
}
}
}
2、树的最小深度(LeetCode题库111题)

class Solution {
public int minDepth(TreeNode root) {
if(root == null) {
return 0;
}else {
int l = 0;
int r = 0;
l = minDepth(root.left);
r = minDepth(root.right);
if(root.left == null || root.right == null)
return l + r + 1;
else
return Math.min(l,r) + 1;
}
}
}
四、变换思路
1、左叶子之和(LeetCode题库404题)
由于要判断”左“所以需要一个父指针,递归是没有办法来使用父指针的,所以想到非递归遍历树
方法一:非递归法

class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root == null) {
return 0;
}else {
Stack<TreeNode> stack = new Stack<>();
TreeNode p = root;
int sum = 0;
while(!stack.isEmpty() || p != null) {
while(p != null) {
stack.push(p);
p = p.left;
}
if(!stack.isEmpty()) {
p = stack.pop();
if(p.left==null&&p.right==null) {
if(!stack.isEmpty()) {
if(stack.peek().left==p)
sum = sum + p.val;
}
}
p = p.right;
}
}
return sum;
}
}
}
方法二:递归法
虽然递归没办法给我们带来父指针,但是我们可以转换一下思路,将问题转换成判断结点的左孩子是不是叶子节点

class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root == null) {
return 0;
}else {
int sum = 0;
if(isLeaf(root.left)) {
sum = sum + root.left.val;
}else {
sum = sum + sumOfLeftLeaves(root.left);
}
sum = sum + sumOfLeftLeaves(root.right);
return sum;
}
}
boolean isLeaf(TreeNode leaf) {
if(leaf == null) return false;
else if(leaf.left == null && leaf.right == null)
return true;
else
return false;
}
}
