树的理解(二):递归

翻转树

https://leetcode-cn.com/problems/invert-binary-tree/submissions/
根据翻转树的特征,我们可以发现翻转树就是将每个节点的左子树和右子树调换,那么递归处理每个节点,定义中间变量,将每个节点的左右指针分别指向右子树和左子树。

public TreeNode invertTree(TreeNode root) {
        if(root == null)
            return null;
        TreeNode cur = root.left;
        root.left = root.right;
        root.right = cur;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

合并二叉树

https://leetcode-cn.com/problems/merge-two-binary-trees/
根据题意我们可以发现,如果两个节点都不为空时,将两个节点的值合并,否则当一个节点不为空时,可以直接将该节点作为合并树的节点。
根据这一特点,我们不难写出以下递归退出条件:
当两个节点都为空时,返回null
当一个节点为空时,返回另一个节点;
只有当两个节点都不为空时,才继续遍历下去。

class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if(t1 == null && t2 == null)return null;
        if(t1 == null)return t2;
        if(t2 == null)return t1;    
        TreeNode root = new TreeNode(t1.val + t2.val);
        root.left = mergeTrees(t1.left,t2.left);
        root.right = mergeTrees(t1.right,t2.right);
        return root;
    }
}

路径总和

https://leetcode-cn.com/problems/path-sum/
根据题意得,只有根节点递归至叶子节点且路径和与sum相等时,才返回真。
那么就分别判断根节点的左右子树,没递归到一个节点就减去该节点的值
直到递归到叶子节点且叶子节点的值与剩下的值相等(路径和等于sum)

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if(root == null)return false;
        if(root.left == null && root.right == null) {
            if(sum == root.val)return true;
            else return false;
        }
        return hasPathSum(root.left,sum - root.val) || hasPathSum(root.right,sum - root.val);
    }
}

路径总和 III

https://leetcode-cn.com/problems/path-sum-iii/

(以树的每一个节点做入口) <-> 双重递归

双重递归,思路其实比较清晰,就是以每个节点为开始,分别求以当前节点为起始点的路径中,是否有等于sum的路径和,如果有,就记录一下。
curPathSum(root,sum)的作用就是以当前节点开始求出路径和中是否有满足条件的个数。curPathSum(root,sum) 的路径和一直递归到叶子节点,因为树中的值有正有负,不能因为路径和<0就终止递归。
但是由题意可以看出,这个路径和可以不是从根节点开始的,可以从中间开始,那么就分别求当前左右子树的路径和,最后将三三者汇总。

但是这种解法相当于对树中的每个节点都做一次路径和,效率不高。

class Solution {
    public int pathSum(TreeNode root, int sum) {
        if(root == null) return 0;
        int res = curPathSum(root,sum) + pathSum(root.left,sum) + pathSum(root.right,sum);
        return res;
    }
    private int curPathSum(TreeNode root,int sum) {
        if(root == null) return 0;
        int res = 0;
        if(root.val == sum)res++;
        res += curPathSum(root.left,sum - root.val) + curPathSum(root.right,sum - root.val);
        return res;
    }
}

另一个树的子树

https://leetcode-cn.com/problems/subtree-of-another-tree/

和路径总和 III的解法思路类似。
(以树的每一个节点做入口) <-> 双重递归

s树中的每个节点,都有可能做为s子树的根节点与t的根节点相同。
hasSubtree(TreeNode s, TreeNode t)具体判断了两棵树是否是具有相同结构和节点值的子树。
isSubtree(TreeNode s, TreeNode t)递归地将当前节点,当前节点的左子树,当前节点的右子树分别做了判断。

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if(s == null)return false;
        return hasSubtree(s,t) || isSubtree(s.left,t) || isSubtree(s.right,t);
    }
    private boolean hasSubtree(TreeNode s, TreeNode t) {
        if(s == null && t == null)return true;
        if(s == null || t == null)return false;
        if(s.val != t.val)return false;
        return hasSubtree(s.left,t.left) && hasSubtree(s.right,t.right);
    }
}

左叶子之和

https://leetcode-cn.com/problems/sum-of-left-leaves/submissions/

判断叶子节点之和,最好在父节点判断,如果到了叶子节点再判断时就无法确定该节点到底是左还是右。
下面两种方法哪个顺手用拿个。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        int[] res = {0};
        sumOfLeftLeaves(root,res);
        return res[0];
    }
    private void sumOfLeftLeaves(TreeNode node,int[] res) {
        if(node == null) return;
        if(isLeaf(node.left)) res[0] += node.left.val;
        sumOfLeftLeaves(node.left,res);
        sumOfLeftLeaves(node.right,res);
    }
    private boolean isLeaf(TreeNode node) {
        if(node == null) return false;
        return node.left == null && node.right == null;
    }
}

另一种解法:

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null)return 0;
        if(isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right);
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
    }

    private boolean isLeaf(TreeNode node) {
        if(node == null) return false;
        return node.left == null && node.right == null;
    }
}

对称二叉树

https://leetcode-cn.com/problems/symmetric-tree/submissions/

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null)return true;
        return (isSymmetric(root.left,root.right));
    }

    private boolean isSymmetric(TreeNode left,TreeNode right) {
        if(left == null && right == null)return true;
        if(left == null || right == null)return false;
        if(left.val != right.val)return false;
        return isSymmetric(left.right,right.left) && isSymmetric(left.left,right.right);
    }
}

最长同值路径

https://leetcode-cn.com/problems/longest-univalue-path/

具体思路是根据根节点来判断左右子节点是否和根节点相等,如果相等则表示是一个路径,否则路径不是一个路径。findPath的返回值表示子节点(左子右子中选一个较长的)路径中的最长路径。

class Solution {
    public int longestUnivaluePath(TreeNode root) {
        int[] res = {0};
        findPath(root,res);
        return res[0];
    }
    private int findPath(TreeNode root,int[] res) {
        if(root == null) return 0;
        int left = findPath(root.left,res);
        int right = findPath(root.right,res);
        int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0;
        int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0;
        res[0] = Math.max(res[0],leftPath + rightPath);
        return Math.max(leftPath,rightPath);
    }
}
发布了352 篇原创文章 · 获赞 73 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43777983/article/details/105003674