【训练营day17|二叉树|110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和】

本文介绍了三个关于二叉树的问题:110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和。平衡二叉树通过自下而上的后序遍历来判断,二叉树的所有路径利用前序遍历和回溯记录路径,左叶子之和则结合后序遍历求解。每个问题都涉及递归和树的遍历策略,以及不同的时间复杂度和空间复杂度。
摘要由CSDN通过智能技术生成

训练营day17|二叉树|110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和

110.平衡二叉树

要点

  1. 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
  2. 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。注:leetcode中强调的深度和高度很明显是按照节点来计算的
  3. 一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
  4. 采用自下而上的遍历,即后序遍历的方式递归。对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 −1。如果存在一棵子树不平衡,则整个二叉树一定不平衡。
  5. 如果自上而下的递归需要时间复杂度 O(N^2);
  6. 时间复杂度 O(N);空间复杂度 O(N);

代码

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        return self.hight(root) != -1
    def hight(self,root):#自下而上的遍历,采用后序遍历
        if not root:
            return 0
        left = self.hight(root.left)
        if left == -1:
            return -1
        right = self.hight(root.right)
        if right == -1 or abs(left - right) > 1:
            return -1
        return max(left,right) + 1

257. 二叉树的所有路径

要点

  1. 递归法二步法
    1)找出重复的子问题。本题采用自上而下的遍历方式,即前序遍历的顺序是:根节点、左子树、右子树。在本题同样也是这个顺序:将根节点加入路径,递归左子树,递归右子树。递归思想是对于根节点而言,它的路径为左子树和右子树所有路径加根节点
    2)确定终止条件。对于二叉树的所有路径中的每条路径,当遍历到叶子节点的时候为当前路径的结束。并且将当前路径加入结果集。
  2. 回溯:这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
    在这里插入图片描述
  3. 如果递归时采用了path + ‘->’,即复制赋值,即达到了隐藏回溯的效果。
  4. 迭代法:用两个栈,一个记录迭代栈,一个栈记录路径栈。
    1)初始化维护两个栈:一个栈是递归栈,将根节点入栈,另一个栈是路径栈,将根节点的值入栈。
    2)当栈不为空时,弹出两个栈的栈顶元素:
    若 node 为叶子节点,将路径加入结果集。
    若 node 的右子树不为空,右孩子及右孩子的值分别入递归栈和路径栈。
    若 node 的左子树不为空,左孩子及左孩子的值分别入递归栈和路径栈。

代码

class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        if not root:
            return []
        path = ''
        result = []
        self.getPaths(root, path, result)
        return result
    
    def getPaths(self, root, path, result):
        path += str(root.val)
        if not root.left and not root.right:
            result.append(path)
        if root.left:
            self.getPaths(root.left, path + '->', result) #path + '->'相当于每次都是复制赋值,不用使用引用,达到了隐藏回溯的效果。如果使用的是引用赋值,即直接path,则每次递归后需要用pop来回溯
        if root.right:
            self.getPaths(root.right, path + '->', result)

404.左叶子之和

要点

  1. 左叶子节点:父节点的左节点不为空,且左节点的左右子树均为空。
  2. 关键在于:判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。因为当前节点可能是父节点的右节点但左右孩子的节点都为空,即右叶子节点。
  3. 待理解的一句话:递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。为什么采用前序遍历这里会有差别?
  4. 时间复杂度:O(n);空间复杂度:O(n)
  5. 迭代法:比较简单,采用前序遍历

代码

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:#采用后序遍历
        if not root:
            return 0
        if not root.left and not root.right:
            return 0
        if root.left and not root.left.left and not root.left.right:#找到了左叶子节点,记录值
            left_value = root.left.val
        else:
            left_value = self.sumOfLeftLeaves(root.left)#递归左子树
        right_value = self.sumOfLeftLeaves(root.right)#递归右子树
        return  left_value + right_value #相加则为当前节点树的左叶子之和
class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:#采用迭代法
        if not root:
            return 0
        stack = [root]
        res = 0
        while stack:
            cur = stack.pop()#每次都将节点的左右节点加入栈中
            if cur.left and not cur.left.left and not cur.left.right:#找到一个左叶子节点
                res += cur.left.val
            if cur.left:
                stack.append(cur.left)
            if cur.right:
                stack.append(cur.right)
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值