二叉树算法练习递归类型汇总
递归类型
按照编程技巧分啪
一、将复杂问题分解成两个子问题
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; } }