Day17 算法学习|二叉树04 继续递归

题目 110.平衡二叉树

问题描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

解题思路请添加图片描述

  • 该代码通过递归地计算每个节点的高度,并比较左右子树的高度差来判断二叉树是否平衡。
  • 如果任何节点的左右子树高度差大于1,或者任何子树不平衡,就返回-1表示不平衡;否则,返回对应的节点高度表示平衡。
  • 判断根节点的高度是否为-1来确定整个二叉树是否平衡。

代码

  • 递归法
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if self.get_height(root) != -1:
            return True
        else:
            return False

    def get_height(self, root: TreeNode) -> int:
        # Base Case
        if not root:
            return 0
        # 左
        if (left_height := self.get_height(root.left)) == -1:
            return -1
        # 右
        if (right_height := self.get_height(root.right)) == -1:
            return -1
        # 中
        if abs(left_height - right_height) > 1:
            return -1
        else:
            return 1 + max(left_height, right_height)

  • 递归法精简

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        return self.height(root) != -1
    def height(self, node: TreeNode) -> int:
            if not node:
                return 0
            left = self.height(node.left)
            if left == -1:
                return -1
            right = self.height(node.right)
            if right == -1 or abs(left - right) > 1:
                return -1
            return max(left, right) + 1
  • 迭代

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        if not root:
            return True

        height_map = {}
        stack = [root]
        while stack:
            node = stack.pop()
            if node:
                stack.append(node)
                stack.append(None)
                if node.left: stack.append(node.left)
                if node.right: stack.append(node.right)
            else:
                real_node = stack.pop()
                left, right = height_map.get(real_node.left, 0), height_map.get(real_node.right, 0)
                if abs(left - right) > 1:
                    return False
                height_map[real_node] = 1 + max(left, right)
        return True

复杂度分析

  • 时间复杂度O(n)

  • 对于每个节点,get_height函数都会被调用一次,所以递归的时间复杂度为O(n),其中n是二叉树中的节点数。
    在每个节点上,计算左子树和右子树的高度以及比较它们的高度差都需要O(1)的时间。

  • 空间复杂度O(n)

  • 递归调用会占用栈空间,其最大深度取决于二叉树的高度。在最坏情况下,如果二叉树是一个斜树(即只有左子树或只有右子树),则递归的最大深度为n。
    在每个递归层级上,除了递归调用本身外,没有使用额外的空间。
    因此,递归调用的空间复杂度为O(n)。
    此外,代码中使用了常量空间来存储局部变量和返回值,不随输入规模变化,因此不会影响空间复杂度。
    综上所述,整体的空间复杂度为O(n)。

题目 404.左叶子之和

问题描述

计算给定二叉树的所有左叶子之和。
在这里插入图片描述

解题思路

  • 递归遍历二叉树的每个节点。
  • 对于当前节点,如果它存在左子节点并且左子节点是叶子节点(即没有左右子节点),则将左子节点的值加到 res 中。
  • 递归调用 sumOfLeftLeaves 函数分别在左子树和右子树上执行相同的操作。
  • 返回最终的结果 res,即为二叉树中所有左叶子节点的值之和。

代码

  • 递归

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        res = 0
        if not root:
            return 0
        
        # Check if the left child is a left leaf
        if root.left and not root.left.left and not root.left.right:
            res += root.left.val
        
       
        res += self.sumOfLeftLeaves(root.left)
        
        
        res += self.sumOfLeftLeaves(root.right)
        
        return res
  • 迭代

class Solution:
    def sumOfLeftLeaves(self, root: TreeNode) -> int:
        """
        Idea: Each time check current node's left node. 
              If current node don't have one, skip it. 
        """
        stack = []
        if root: 
            stack.append(root)
        res = 0
        
        while stack: 
            # 每次都把当前节点的左节点加进去. 
            cur_node = stack.pop()
            if cur_node.left and not cur_node.left.left and not cur_node.left.right: 
                res += cur_node.left.val
                
            if cur_node.left: 
                stack.append(cur_node.left)
            if cur_node.right: 
                stack.append(cur_node.right)
                
        return res

复杂度分析(递归)

  • 时间复杂度O(n)

    对于每个节点,我们需要判断它是否是左叶子节点,这需要O(1)的时间。
    在每个节点上,我们递归调用sumOfLeftLeaves函数来计算左子树和右子树的左叶子节点值之和。
    假设二叉树中有n个节点,每个节点最多被访问一次。
    因此,总的时间复杂度为O(n)。

  • 空间复杂度O(n)

  • 递归调用会占用栈空间,其最大深度取决于二叉树的高度。在最坏情况下,如果二叉树是一个斜树(即只有左子树或只有右子树),递归的最大深度为n。
    在每个递归层级上,除了递归调用本身外,没有使用额外的空间。
    因此,递归调用的空间复杂度为O(n)。
    此外,代码中使用了常量空间来存储局部变量和返回值,不随输入规模变化,因此不会影响空间复杂度。
    综上所述,整体的空间复杂度为O(n)。

题目 257. 二叉树的所有路径

问题描述

给定一个二叉树,返回所有从根节点到叶子节点的路径。
在这里插入图片描述

解题思路

代码

  • 递归
import copy
from typing import List, Optional

class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        if not root:
            return []
        result = []
        self.generate_paths(root, [], result)
        return result
    
    def generate_paths(self, node: TreeNode, path: List[int], result: List[str]) -> None:
        path.append(node.val)
        if not node.left and not node.right:
            result.append('->'.join(map(str, path)))
        if node.left:
            self.generate_paths(node.left, copy.copy(path), result)
        if node.right:
            self.generate_paths(node.right, copy.copy(path), result)
        path.pop()


  • 递归2

import copy
from typing import List, Optional

class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        if not root:
            return []
        result = []
        self.generate_paths(root, [], result)
        return result
    
    def generate_paths(self, node: TreeNode, path: List[int], result: List[str]) -> None:
        if not node:
            return
        path.append(node.val)
        if not node.left and not node.right:
            result.append('->'.join(map(str, path)))
        else:
            self.generate_paths(node.left, copy.copy(path), result)
            self.generate_paths(node.right, copy.copy(path), result)
        path.pop()

  • 递归法+隐形回溯
class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        path = ''
        result = []
        if not root: return result
        self.traversal(root, path, result)
        return result
    
    def traversal(self, cur: TreeNode, path: str, result: List[str]) -> None:
        path += str(cur.val)
        # 若当前节点为leave,直接输出
        if not cur.left and not cur.right:
            result.append(path)

        if cur.left:
            # + '->' 是隐藏回溯
            self.traversal(cur.left, path + '->', result)
        
        if cur.right:
            self.traversal(cur.right, path + '->', result)

  • 迭代法
class Solution:
    """二叉树的所有路径 迭代法"""

    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        # 题目中节点数至少为1
        stack, path_st, result = deque([root]), deque(), []
        path_st.append(str(root.val))

        while stack:
            cur = stack.pop()
            path = path_st.pop()
            # 如果当前节点为叶子节点,添加路径到结果中
            if not (cur.left or cur.right):
                result.append(path)
            if cur.right:
                stack.append(cur.right)
                path_st.append(path + '->' + str(cur.right.val))
            if cur.left:
                stack.append(cur.left)
                path_st.append(path + '->' + str(cur.left.val))

        return result

复杂度分析

  • 时间复杂度 O(n log(n))

    对于每个节点,我们需要将其值添加到路径列表中,这需要 O(1) 的时间。
    对于每个叶子节点,我们需要将路径列表转换为字符串形式,并将其添加到结果列表中。转换为字符串的过程需要 O(k) 的时间,其中 k 是路径的长度。
    在最坏情况下,树是一个完全二叉树,共有 n/2 个叶子节点,每个叶子节点的路径长度最长为 log(n)。
    因此,总的时间复杂度为 O(n log(n))。

  • 空间复杂度O(n)

  • 递归调用会占用栈空间,其最大深度取决于二叉树的高度。在最坏情况下,如果二叉树是一个链式结构(即只有左子树或只有右子树),递归的最大深度为 n。
    在每个递归层级上,除了递归调用本身外,占用的额外空间包括路径列表 path 和结果列表 result。
    路径列表的长度最大为树的高度,因此空间复杂度为 O(n)。
    结果列表中保存了所有路径的字符串形式,最多有 n/2 个叶子节点,每个叶子节点的路径长度最长为 log(n)。
    转换为字符串形式的过程中,额外空间的消耗取决于最长的路径长度,因此空间复杂度为 O(n log(n))。
    综上所述,整体的空间复杂度为 O(n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值