二叉树算法练习递归类型汇总

递归类型

按照编程技巧分啪

一、将复杂问题分解成两个子问题

 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;
        }
    }
}
View Code

 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);
        }
    }
}
View Code

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;
        }
    }
}
View Code

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);
            }
        }
    }
}
View Code

二、"||" 与 "&&"

这一类题,是说左右子树有一个满足就返回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;
        }
    }
}
View Code

 四、变换思路

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;
        }
    }
}
View Code

 

方法二:递归法

虽然递归没办法给我们带来父指针,但是我们可以转换一下思路,将问题转换成判断结点的左孩子是不是叶子节点

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;
    }
}
View Code

 

posted @ 2019-10-13 21:34  卑微芒果  Views(467)  Comments(0Edit  收藏  举报