用二叉树的递归算法解决问题(LeetCode)

概述

二叉树的递归算法可以分为两种,top-downbottom-up

“Top-down” Solution

“Top-down” means that in each recursive call, we will visit the node first to come up with some values, and pass these values to its children when calling the function recursively. So the “top-down” solution can be considered as a kind of preorder traversal.

“Bottom-up” Solution

“Bottom-up” is another recursive solution. In each recursive call, we will firstly call the function recursively for all the children nodes and then come up with the answer according to the returned values and the value of the current node itself. This process can be regarded as a kind of postorder traversal.

方法选择

When you meet a tree problem, ask yourself two questions:

  • Can you determine some parameters to help the node know its answer?
  • Can you use these parameters and the value of the node itself to determine what should be the parameters passed to its children?

If the answers are both yes, try to solve this problem using a “top-down” recursive solution.

Or, you can think of the problem in this way:

  • for a node in a tree, if you know the answer of its children, can you calculate the answer of that node?

If the answer is yes, solving the problem recursively using a “bottom-up” approach might be a good idea.

例题一:二叉树的最大深度

在这里插入图片描述

top-down recursive solution

We know that the depth of the root node is 1. For each node, if we know its depth, we will know the depth of its children. Therefore, if we pass the depth of the node as a parameter when calling the function recursively, all the nodes will know their depth. And for leaf nodes, we can use the depth to update the final answer.

class Solution {
        //top-down recursive solution
        int ans = 0;
    public int maxDepth(TreeNode root) {
            find(root, 1);
            return ans;
    }
        public void find(TreeNode node, int depth){
                if(node == null)
                        return;
                if(node.left == null && node.right == null)
                        ans = Math.max(depth, ans);
                find(node.left, depth+1);
                find(node.right, depth+1);
        }

}

bottom-up recursive solution

Let’s go on discussing the question about maximum depth but using a different way of thinking: for a single node of the tree, what will be the maximum depth x of the subtree rooted at itself?

If we know the maximum depth l of the subtree rooted at its left child and the maximum depth r of the subtree rooted at its right child, can we answer the previous question? Of course yes, we can choose the maximum between them and add 1 to get the maximum depth of the subtree rooted at the current node. That is x = max(l, r) + 1.

It means that for each node, we can get the answer after solving the problem for its children. Therefore, we can solve this problem using a “bottom-up” solution.

class Solution {
        //bottom-up recursive solution
    public int maxDepth(TreeNode root) {
            int ans = find(root);
            return ans;
    }
        public int find(TreeNode node){
                if(node == null)
                        return 0;
                int leftAns = find(node.left);
                int rightAns = find(node.right);
                return Math.max(leftAns, rightAns)+1;
        }

}

例题二:对称树

在这里插入图片描述

递归方法(bottom-up)

相当于比较root的左子树和右子树是否是镜像的,如下图所示。比较方法则是比较每一个左子树的节点t1,与对应的右子树的节点t2,看t1.left与t2.right,以及t1.right与t2.left是否都相等。
在这里插入图片描述

class Solution {
    public boolean isSymmetric(TreeNode root) {
            if(root == null)
                    return true;
            return isMirror(root.left, root.right);
            
        
    }
        public boolean isMirror(TreeNode t1, TreeNode t2){
                if(t1 == null && t2 == null)
                        return true;
                if(t1 == null || t2 == null)
                        return false;
                return (t1.val == t2.val) && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
        }
}

时间复杂度和空间复杂度都为O(N)。

迭代方法

值得注意的是,当queue.add()的对象为null时,仍会添加成功,queue.size()加1,null也可被queue.remove(),queue.size()减1。
经测试,对于链表,当添加对象为null时,null作为一个对象被添加到链表中。

class Solution {
    public boolean isSymmetric(TreeNode root) {
            if(root == null)
                    return true;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.add(root.left);
            queue.add(root.right);
            TreeNode t1, t2;
            while(!queue.isEmpty()){
                    t1 = queue.remove();
                    t2 = queue.remove();
                    if (t1 == null && t2 == null) continue;
                    if(t1 == null || t2 == null)
                            return false;
                    if(t1.val != t2.val)
                            return false;
                    queue.add(t1.left);
                    queue.add(t2.right);
                    queue.add(t1.right);
                    queue.add(t2.left);
            }
            return true;
        }
}

例题三:路径之和

在这里插入图片描述

top-down recursive solution

class Solution {
    boolean isSum = false;
    public boolean hasPathSum(TreeNode root, int sum) {
            if(root == null)
                    return isSum;
            helper(root, sum);
            return isSum;

    }
        public void helper(TreeNode node, int sum){
                if(node == null)
                        return;
                if(node.left ==null && node.right == null && (sum-node.val) == 0)
                        isSum = true;
                helper(node.left, sum-node.val);
                helper(node.right, sum-node.val);
        }
}

bottom-up recursive solution

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

例题四:计算 Univalue Subtrees

在这里插入图片描述

bottom-up recursive solution

Given a node in our tree, we know that it is a univalue subtree if it meets one of the following criteria:

  • The node has no children (base case)
  • All of the node’s children are univalue subtrees, and the node and its children all have the same value

With this in mind we can perform a depth-first-search on our tree, and test if each subtree is uni-value in a bottom-up manner.

自己编写的算法(有点长),这段代码中的注释一定要注意。

    class Solution {
        int count = 0;

        public int countUnivalSubtrees(TreeNode root) {
            helper(root);
            return count;
        }

        public boolean helper(TreeNode node) {
            if (node == null)
                return true;
                //注意!下面这句的&不可以换成&&,如果换成&&就会导致当根节点的左子树返回值为false时,不遍历右子树。
            if (helper(node.left) & helper(node.right)) {
                if (node.left != null && node.right != null) {
                    if (node.left.val == node.right.val && node.val == node.left.val) {
                        count++;
                        return true;
                    }
                }
                if (node.left == null && node.right == null) {
                    count++;
                    return true;
                }
                if (node.left == null && node.val == node.right.val) {
                    count++;
                    return true;
                }
                if (node.right == null && node.val == node.left.val) {
                    count++;
                    return true;
                }
            }
            return false;
        }
    }

同样的思路,代码改进版:

    class Solution {
        int count = 0;

        public int countUnivalSubtrees(TreeNode root) {
                if(root == null)
                        return count;
            helper(root);
            return count;
        }

        public boolean helper(TreeNode node) {
                if(node.left == null && node.right == null){
                        count++;
                        return true;
                }
                boolean isUni = true;
                if (node.left != null) {
                        isUni = helper(node.left) && (node.left.val == node.val);
                }
                if (node.right != null) {
                        isUni = helper(node.right) && isUni && (node.right.val == node.val);
                }
                if (isUni) {
                    count++;
                    return true;
                }
                return false;
                
        }
    }

进一步精简版,将null看做一个univalue tree,返回true,但是不count++。

    class Solution {
        int count = 0;

        public int countUnivalSubtrees(TreeNode root) {
            helper(root, 0);
            return count;
        }

        public boolean helper(TreeNode node, int value) {
                if(node == null)
                        return true;
                        //注意下面用的是|而不是||
                if(!helper(node.left, node.val) | !helper(node.right, node.val))
                        return false;
                count++;
                return node.val == value;
                
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值