美文网首页
二叉树写递归,这样写才快准狠 2019-11-25(未经允许,禁

二叉树写递归,这样写才快准狠 2019-11-25(未经允许,禁

作者: 9_SooHyun | 来源:发表于2019-11-26 00:47 被阅读0次

    由于树本身是递归定义的特性,树问题往往都可以通过写递归来解决。但是如何写对和写好这个递归,是比较困难的一点

    之前写过一篇博客介绍自己写递归的方式,点这里
    递归要搞清楚2件事,函数做什么事(分段函数做事)+ 函数返回什么值
    现在就二叉树问题再具体地看一下

    引入

    先从简单的引入,前序遍历二叉树相信大家都非常熟悉了

    def preOrder(Tree node):
        if node == None:
            return
        visit(node.data)
        preOrder(node.leftchild)
        preOrder(node.rightchild)
    

    在这里,函数做事情的分段函数就是
    # 出口段
    if node == None:
    return
    # 继续段
    visit(node.data)
    preOrder(node.leftchild)
    preOrder(node.rightchild)

    其实,二叉树的递归出口一般都比较容易写,一般都是 node == None 或者 具体问题下某些特定终止的情况

    难就难在继续段要做什么事,怎么写代码。所有二叉树问题递归形式的继续段有没有什么共通的特点呢?

    有的

    金道理1:要从树的根节点root开始,通过不断递归实现对整棵树的有规律的访问,就必须对root的所有孩子结点做递归对于二叉树而言,在root结点对应的函数体中,必须包含对root.left和root.right的递归,才能保证对整棵树进行完整的递归访问

    如先序遍历中的preOrder(node.leftchild)preOrder(node.rightchild)

    以上是得出的写二叉树递归的共性结论
    下面用一些具体问题巩固理解

    深入

    1. 给定一个二叉树,找出其最大深度

    思路

    • 确定函数返回值:
      返回以传入的参数结点root为根结点的树的深度

    • 找分段函数:

      • 出口段:显然,当传入的参数结点root为None时,直接返回0
      • 继续段:递归访问左孩子结点,可得到左子树的深度;递归访问右孩子结点,可得到右子树的深度;返回 1 + max(左深,右深)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def maxDepth(self, root: TreeNode) -> int:
            if not root:
                return 0
            return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
    

    2. 给定一个非空二叉树,返回其最大路径和

    示例:


    输出: 42(15+20+7)

    思路

    • 确定函数返回值:
      返回以传入参数结点root为根结点的最长一支(左支或者右支)的长度和,而不是以传入的参数结点root为根结点的树的最大路径和,即不直接返回题目的所求
      思考:为什么这里不能像上题那样让函数直接返回题目所求呢?什么情况下可以直接返回所求,什么情况下不可以呢?

    • 找分段函数:

      • 出口段:显然,和上题一样,当传入的参数结点root为None时,直接返回0
      • 继续段:
        • 递归访问左孩子结点,可得到以左孩子为根结点的最长一支的长度和max_left;
        • 递归访问右孩子结点,可得到以右孩子为根结点的最长一支的长度和max_right;
        • 这时,将树的最长路径和max_len 修改为 (root.val + max_left + max_right, root.val + max_left, root.val + max_right, root.val, max_len)中最大的一个;
        • 最后 return max(root.val + max_left, root.val + max_right, root.val),作为以传入的参数结点root为根结点的最长一支的长度和
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def __init__(self):
            self.max_len = 0
        
        def maxPathSum(self, root: TreeNode) -> int:
            # 先给self.max_len赋有意义的值
            if root:
                self.max_len = root.val
            self.searchMaxBranch(root)
            return self.max_len
        
        # 这里是核心代码
        # 返回经过传入的参数结点root的左右支的最大一支的路径 and 修改树的最大路径长度
        def searchMaxBranch(self, root) -> int:
            if not root:
                return 0
            max_left = self.searchMaxBranch(root.left)
            max_right = self.searchMaxBranch(root.right)
            # 修改self.max_len
            self.max_len = max(root.val + max_left + max_right, root.val + max_left, root.val + max_right, root.val, self.max_len)
            # 返回经过传入的参数结点root的左右支的最大一支的路径
            return max(root.val + max_left, root.val + max_right, root.val)
    

    现在我们回到刚才提出的思考,为什么这里不能让定义的函数直接返回所求呢?什么情况下可以直接返回所求,什么情况下不可以呢?

    金道理2:事实上,通过观察两题的区别可以发现,求树的深度所形成的结果路径是纵向路径,不会经过某个结点的两支;而求树的最长路径所形成的结果路径则会经过某个结点的两支,属于横向路径,如示例中的结果 42 = 15+20+7,15+20+7这条路径经过了结点20的两支。我们知道,树的递归遍历的特点是深度优先,因此树的递归一定只能产生纵向路径,当我们遇到横向路径时,要先等价转换为两条纵向路径之和

    弄懂金道理1 & 金道理2,二叉树写递归,稳了

    相关文章

      网友评论

          本文标题:二叉树写递归,这样写才快准狠 2019-11-25(未经允许,禁

          本文链接:https://www.haomeiwen.com/subject/kfeswctx.html