美文网首页面试精选
LeetCode 二叉树和递归专题 1:从二叉树的角度看递归

LeetCode 二叉树和递归专题 1:从二叉树的角度看递归

作者: 李威威 | 来源:发表于2019-05-29 06:49 被阅读0次

    我们开始介绍“二叉树和递归”。递归,是使用计算机解决问题的一种重要的思考方式。而二叉树由于其天然的递归结构,使得基于二叉树的算法,均拥有着递归性质。使用二叉树,是研究学习递归算法的最佳入门方式。在这一章里,我们就来看一看二叉树中的递归算法。

    在前面知识的学习中,我们看到了在基础算法以及系统设计中都用到了递归。深度优先遍历中也用到了递归。从这一部分开始,我们从另一个视角看递归。

    从二叉树的角度看递归

    二叉树天然具有递归的性质。二叉树的定义就是用二叉树定义二叉树。对于二叉树的定义来说,应该补充一点:空是一棵二叉树。

    下面,我们来观察一个二叉树的前序遍历的递归方法。

    Java 代码:

    public void preorder(TreeNode node){
        if(node != null){
            System.out.print(node.val);
            preorder(node.left);
            preorder(node.right);
        } 
    }
    

    注意:这里 System.out.print(node.val); 这一行代码表示“处理这个结点的逻辑”。

    在这里,我们先强调编写递归函数的第 1 个注意事项:首先明确这个函数要表达的任务(逻辑),明确参数的定义和返回值的意义。

    接下来,我们将上面的代码改造一下。

    Java 代码:

    public void preorder(TreeNode node){
        
        // 先写递归终止条件
        if(node == null){
            return;
        } 
        
        // 再写递归过程
        System.out.print(node.val);
        preorder(node.left);
        preorder(node.right);
    }
    

    其实这样的写法更像递归结构,因为对于我们定义的每一个递归函数来说,应该包含下面两个部分:1、递归终止条件;2、设立递归过程,定义清楚函数的语义。

    这就是我们编写一个递归函数应该注意的第 2 件事情。

    接下来,我们再看一个方法:在一个二叉树中查看是否存在一个键值。写这个递归方法的步骤:想想(1)递归终止条件是什么?(2)递归过程是什么?参数 Node node 是什么意思?我们这里定义的参数 Node 对象,是在以 node 为根结点的二叉树中寻找指定的 key。于是,我们的逻辑是:如果 node 本身不是我们要找的结点的话,我们继续在 node 的左孩子和右孩子中继续查找。

    Java 代码:

    public boolean contain(Node node, int key) {
        // 首先我们要处理递归到底的情况
        if (node == null) {
            return false;
        }
        
        if (key == node.key) {
            return true;
        } 
        
        if (contain(node.left, key) || contain(node.right, key) {
            return true;
        } 
        return false;
    }
    

    那么,我们可以再想想如何释放以 Node 为根结点的二叉树?

    例题

    例1:LeetCode 第 104 题:求一棵二叉树的最大深度。

    传送门:英文网址:104. Maximum Depth of Binary Tree ,中文网址:104. 二叉树的最大深度

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

    二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

    说明: 叶子节点是指没有子节点的节点。

    示例:
    给定二叉树 [3,9,20,null,null,15,7]

        3
       / \
      9  20
        /  \
       15   7
    

    返回它的最大深度 3 。

    关键:要能看出这道题本质上是二叉树的后序遍历。

    Python 代码:

    # Definition for a binary tree node.
    # class TreeNode(object):
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    
    class Solution(object):
        def maxDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
            if root is None:
                return 0
            # 先计算左右子树,然后再计算自己,这是后序遍历
            l_sub_tree_depth = self.maxDepth(root.left)
            r_sub_tree_depth = self.maxDepth(root.right)
            return max(l_sub_tree_depth, r_sub_tree_depth) + 1
    

    Python 代码:把上面的递归改成循环

    class Solution(object):
        def maxDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
            if root is None:
                return 0
            depth = 0
            stack = [(1, root)]
            while stack:
                cur_depth, node = stack.pop()
                depth = max(depth, cur_depth)
                if node.left:
                    stack.append((cur_depth + 1, node.left))
                if node.right:
                    stack.append((cur_depth + 1, node.right))
            return depth
    

    说明:这个写法看一看就好,感觉没啥意思。

    感觉递归调用就像什么都没有做一样。通过这个例子,我们来理解一下(1)(2)这两个步骤的具体应用。这让我想起了八皇后问题。

    还可以使用 DFS 和 BFS 完成这个问题。首先 BFS 我觉得思路更直接一些,代码也是有套路的。

    Python 代码:

    class Solution(object):
        def maxDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
            if root is None:
                return 0
            depth = 0
            queue = [root]
            while queue:
                size = len(queue)
                depth += 1
                for _ in range(size):
                    first = queue.pop(0)
                    if first.left:
                        queue.append(first.left)
                    if first.right:
                        queue.append(first.right)
            return depth
    

    Python 代码:使用 DFS

    # Definition for a binary tree node.
    # class TreeNode(object):
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    # 求一棵二叉树的最大深度。
    # 要能看出这道题本质上是二叉树的后序遍历。
    
    
    class Solution(object):
    
        def __init__(self):
            self.max_depth = 0
    
        def maxDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
            if root is None:
                return 0
            self.__dfs(root, 0)
            return self.max_depth
    
        def __dfs(self, node, depth):
            if node is None:
                return
            depth += 1
            if node.left is None and node.right is None:
                # 到叶子结点了,可以结算了
                self.max_depth = max(self.max_depth, depth)
                return
            if node.left:
                self.__dfs(node.left, depth)
            if node.right:
                self.__dfs(node.right, depth)
    
    • 复习和二叉树相关的所有操作。

    练习

    练习1:LeetCode 第 111 题:求一棵二叉树的最小深度。

    传送门:英文网址:111. Minimum Depth of Binary Tree ,中文网址:111. 二叉树的最小深度

    给定一个二叉树,找出其最小深度。

    最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

    说明: 叶子节点是指没有子节点的节点。

    示例:

    给定二叉树 [3,9,20,null,null,15,7],

        3
       / \
      9  20
        /  \
       15   7
    

    返回它的最小深度 2.

    分析:即求一棵二叉树从根结点到叶子结点的最短路径的长度。

    • 这个问题里面有小的陷阱。

    • 我们在思考递归终止条件的时候,有的时候可能会存在陷阱。

    LeetCode 第 111 题:求一棵二叉树的最小深度

    这道题,我第一次做是想当然,顺着第 104 题把最大改成最小,但是要注意到上图 4 那个结点,4 的左孩子为空,返回 0 ,右孩子为 9,返回2,按照我们的逻辑就返回 0 ,显然是错误的,所以要针对左右孩子有一个为空的时候,做出分类判断。

    Python 代码:

    # Definition for a binary tree node.
    # 111. 二叉树的最小深度
    # 给定一个二叉树,找出其最小深度。
    #
    # 最小深度是从根结点到最近叶子结点的最短路径上的结点数量。
    #
    # 说明: 叶子结点是指没有子结点的结点。
    
    
    class TreeNode(object):
        def __init__(self, x):
            self.val = x
            self.left = None
            self.right = None
    
    
    class Solution(object):
        def minDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
    
            if root is None:
                return 0
    
            if root.left is None:
                return 1 + self.minDepth(root.right)
    
            if root.right is None:
                return 1 + self.minDepth(root.left)
    
            return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
    

    Python 代码:使用 BFS:==使用层序遍历,我感觉更直接一些,因为只要扫到叶子结点,就可以返回了==

    class Solution(object):
        def minDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
    
            if root is None:
                return 0
            depth = 0
            queue = [root]
            while queue:
                depth += 1
                size = len(queue)
                for _ in range(size):
                    first = queue.pop(0)
                    if first.left is None and first.right is None:
                        return depth
                    if first.left:
                        queue.append(first.left)
                    if first.right:
                        queue.append(first.right)
    

    Python 代码:使用 DFS

    # Definition for a binary tree node.
    # 111. 二叉树的最小深度
    # 给定一个二叉树,找出其最小深度。
    #
    # 最小深度是从根结点到最近叶子结点的最短路径上的结点数量。
    #
    # 说明: 叶子结点是指没有子结点的结点。
    
    
    class TreeNode(object):
    
        def __init__(self, x):
            self.val = x
            self.left = None
            self.right = None
    
    
    class Solution(object):
    
        def __init__(self):
            self.min_depth = float("inf")
    
        def minDepth(self, root):
            """
            :type root: TreeNode
            :rtype: int
            """
    
            if root is None:
                return 0
    
            self.__dfs(root, 0)
            return self.min_depth
    
        def __dfs(self, node, depth):
            if node is None:
                return
            depth += 1
            if node.left is None and node.right is None:
                self.min_depth = min(self.min_depth, depth)
                return
            if node.left:
                self.__dfs(node.left, depth)
            if node.right:
                self.__dfs(node.right, depth)
    

    (本节完)

    相关文章

      网友评论

        本文标题:LeetCode 二叉树和递归专题 1:从二叉树的角度看递归

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