代码随想录训练营 Day15打卡 二叉树 part03 110.平衡二叉树 257. 二叉树的所有路径 404.左叶子之和 222.完全二叉树的节点个数

代码随想录训练营 Day15打卡 二叉树 part03

一、 力扣110. 平衡二叉树

给定一个二叉树,判断它是否是 平衡二叉树
示例 :

输入 :root = [3,9,20,null,null,15,7]
输出:true

这里强调一波概念:

二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。

二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。

但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:

思路与递归分析

给定一个二叉树,判断它是否是平衡二叉树。平衡二叉树定义为每个节点的左右子树高度差不超过1。

递归三步曲分析

  1. 明确递归函数的参数和返回值:

    参数:当前传入节点。

    返回值:以当前传入节点为根节点的树的高度。如果已经不是平衡树,返回-1。

  2. 明确终止条件:

    当节点为空时,返回高度0。

  3. 明确单层递归的逻辑:

    分别求出左右子树的高度。

    如果左右子树的高度差大于1,返回-1。

    否则,返回当前树的高度(即左右子树高度的最大值加1)。

代码实现

版本一 递归法

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        # 调用 get_height 函数,如果返回值不是 -1,说明是平衡二叉树
        return self.get_height(root) != -1

    def get_height(self, root: TreeNode) -> int:
        # 基本情况:如果节点为空,返回高度 0
        if not root:
            return 0

        # 递归求左子树高度
        left_height = self.get_height(root.left)
        # 如果左子树已经不是平衡树,直接返回 -1
        if left_height == -1:
            return -1

        # 递归求右子树高度
        right_height = self.get_height(root.right)
        # 如果右子树已经不是平衡树,直接返回 -1
        if right_height == -1:
            return -1

        # 当前节点:判断左右子树高度差是否超过 1
        if abs(left_height - right_height) > 1:
            return -1
        else:
            # 如果高度差在允许范围内,返回当前树的高度
            return 1 + max(left_height, right_height)

版本二 递归法 精简版

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        # 调用 get_height 函数,如果返回值不是 -1,说明是平衡二叉树
        return self.get_height(root) != -1

    def get_height(self, node: TreeNode) -> int:
        # 基本情况:如果节点为空,返回高度 0
        if not node:
            return 0

        # 递归求左子树高度
        left = self.get_height(node.left)
        # 递归求右子树高度
        right = self.get_height(node.right)

        # 判断左右子树高度差是否超过 1 或者左右子树已经不是平衡树
        if left == -1 or right == -1 or abs(left - right) > 1:
            return -1
        
        # 返回当前树的高度
        return max(left, right) + 1

力扣题目链接
题目文章讲解
题目视频讲解

二、 力扣257. 二叉树的所有路径

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 :

输入 :root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

前序遍历以及回溯的过程如图:

递归法

递归解法是比较直观和常见的做法,可以按照前序遍历(根左右)的顺序进行处理。

  1. 定义递归函数 traversal(cur, path, result):

    cur 是当前节点。

    path 是从根节点到当前节点的路径。

    result 是存放所有路径的结果列表。

  2. 递归终止条件:

    如果当前节点是叶子节点(即没有左右子节点),将路径转换成字符串格式并加入结果列表。

  3. 递归过程:

    将当前节点的值加入到路径中。

    递归处理左子树和右子树。

    递归完成后,需要进行回溯,即将当前节点从路径中删除,以便处理其他路径。

  4. 边界处理:

    如果根节点为空,则直接返回空列表。

python 复制代码
class Solution:
    def traversal(self, cur, path, result):
        path.append(cur.val)  # 将当前节点值添加到路径中
        # 检查当前节点是否是叶子节点
        if not cur.left and not cur.right:  
        	# 将路径中的值转换成字符串形式
            sPath = '->'.join(map(str, path))  
            # 将路径字符串添加到结果列表中
            result.append(sPath)  
            # 返回前一个节点
            return  
        # 如果当前节点有左子节点
        if cur.left:  
            self.traversal(cur.left, path, result)  # 递归遍历左子树
            path.pop()  # 回溯,移除最后一个元素,回到父节点
		# 如果当前节点有右子节点
        if cur.right:  
            self.traversal(cur.right, path, result)  # 递归遍历右子树
            path.pop()  # 回溯,移除最后一个元素,回到父节点

    def binaryTreePaths(self, root):
        result = []  # 用于存放所有路径的结果列表
        path = []  # 用于存放当前路径的列表
        if not root:
            return result  # 如果根节点为空,直接返回空列表
        self.traversal(root, path, result)  # 从根节点开始递归遍历
        return result

迭代法

迭代解法使用栈来模拟递归的过程,也是按照前序遍历的顺序处理节点。

  1. 使用两个栈 stack 和 path_st 来分别存放当前节点和对应的路径

stack 存放当前处理的节点。

path_st 存放从根节点到当前节点的路径字符串。

  1. 使用循环遍历栈,直到栈为空

弹出当前节点和对应的路径。

如果当前节点是叶子节点,将路径添加到结果列表中。

将右子节点和左子节点(先右后左,保证前序遍历顺序)依次入栈,并更新路径字符串。

  1. 最终返回结果列表。
python 复制代码
class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        if not root:
            return []  # 如果根节点为空,返回空列表
        stack, path_st, result = [root], [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

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣404. 左叶子之和

给定二叉树的根节点 root ,返回所有左叶子之和。
示例:

输入: root = [3,9,20,null,null,15,7]
输出: 24
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点

相信通过这两个图,大家对最左叶子的定义有明确理解了。

版本一 递归法(后序遍历)

实现思路:通过后序遍历递归方法来累加左叶子的数值。此方法中,左子树的递归结果可能需要重新赋值,确保只有左叶子的值被累加。

python 复制代码
class Solution:
    def sumOfLeftLeaves(self, root):
        if root is None:
            return 0  # 如果节点为空,返回0
        if root.left is None and root.right is None:
            return 0  # 如果是叶子节点,返回0

        leftValue = self.sumOfLeftLeaves(root.left)  # 递归左子树求左叶子节点之和
        if root.left and not root.left.left and not root.left.right:
            # 如果左子节点存在且是叶子节点,则累加其值
            leftValue = root.left.val

        rightValue = self.sumOfLeftLeaves(root.right)  # 递归右子树求左叶子节点之和

        sum_val = leftValue + rightValue  # 将左右子树返回的值求和
        return sum_val

版本二 递归法(精简版)

实现思路:这个版本在每一层递归中同时检查并累加左叶子节点的值,并递归处理左右子树。此方法减少了递归的调用层级,提高了效率。

python 复制代码
class Solution:
    def sumOfLeftLeaves(self, root):
        if root is None:
            return 0  # 如果节点为空,返回0
        leftValue = 0
        if root.left and root.left.left is None and root.left.right is None:
            # 检查当前节点的左子节点是否是叶子节点,如果是则直接取值
            leftValue = root.left.val
        # 返回左叶子节点的值加上递归左子树和右子树的左叶子之和
        return leftValue + self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right)

版本三 迭代法

实现思路:使用栈来模拟递归过程,对每个节点进行检查,如果左子节点是叶子节点,则累加其值。此迭代方法有效地避免了递归可能导致的栈溢出问题,适合处理深度较大的树。

python 复制代码
class Solution:
    def sumOfLeftLeaves(self, root):
        if root is None:
            return 0  # 如果根节点为空,返回0
        st = [root]  # 使用栈来进行迭代
        result = 0  # 初始化左叶子节点之和为0
        while st:
            node = st.pop()  # 弹出节点进行处理
            if node.left and node.left.left is None and node.left.right is None:
                # 如果左子节点存在且为叶子节点,则累加其值
                result += node.left.val
            if node.right:
                st.append(node.right)  # 如果右子节点存在,添加到栈中
            if node.left:
                st.append(node.left)  # 如果左子节点存在,添加到栈中
        return result

力扣题目链接
题目文章讲解
题目视频讲解

四、 力扣222. 完全二叉树的节点个数

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例 :

输入: root = [1,2,3,4,5,6]
输出: 6

大家要自己看完全二叉树的定义,很多同学对完全二叉树其实不是真正的懂了。

我来举一个典型的例子如题:

完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。

对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。

对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。

版本一

实现思路:该方法首先通过迭代计算左右子树的最大深度,如果深度相等,则树是满二叉树,可以直接使用公式计算节点总数。如果不是满二叉树,则递归计算左右子树的节点数,并加上根节点。

python 复制代码
class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root:
            return 0  # 如果根节点为空,则节点总数为0
        left = root.left  # 左子树的根节点
        right = root.right  # 右子树的根节点
        leftDepth = 0  # 左子树的深度初始化为0
        rightDepth = 0  # 右子树的深度初始化为0
        while left:  # 计算左子树的最大深度
            left = left.left
            leftDepth += 1
        while right:  # 计算右子树的最大深度
            right = right.right
            rightDepth += 1
        if leftDepth == rightDepth:
            return (2 << leftDepth) - 1  # 如果左右子树深度相等,则是满二叉树
        return self.countNodes(root.left) + self.countNodes(root.right) + 1

版本二

实现思路:这个版本使用了层级计数的方式来检测是否是满二叉树。如果在某一层中同时存在左右子节点,且都到达底部(同时为空),则通过公式计算。否则,递归地计算左右子树的节点数。

python 复制代码
class Solution: 
    def countNodes(self, root: TreeNode) -> int:
        if not root: 
            return 0  # 如果根节点为空,则节点总数为0
        count = 1  # 计数包含根节点
        left = root.left; right = root.right
        while left and right:  # 计算同时有左右子节点的层次
            count += 1
            left = left.left; right = right.right
        if not left and not right:  # 如果左右子树同时为空,则到达了满二叉树的最底部
            return 2**count - 1  # 使用公式计算满二叉树的节点数
        return 1 + self.countNodes(root.left) + self.countNodes(root.right)  # 递归计算左右子树节点数加根节点

力扣题目链接
题目文章讲解
题目视频讲解

相关推荐
学c真好玩几秒前
4.1-python操作wrod/pdf 文件
开发语言·python·pdf
姜行运几秒前
数据结构【链表】
c语言·开发语言·数据结构·链表
东方佑1 分钟前
使用Python解析PPT文件并生成JSON结构详解
python·json·powerpoint
Auroral1564 分钟前
一文搞懂python实现邮件发送的全流程
python
大霸王龙5 分钟前
LLM(语言学习模型)行为控制技术
python·深度学习·学习
我不是大佬zvj7 分钟前
PyGame开发贪吃蛇小游戏
python·pygame
学习2年半13 分钟前
53. 最大子数组和
算法
这里有鱼汤17 分钟前
Python 图像处理必备的 15 个基本技能 🎨
前端·后端·python
这里有鱼汤18 分钟前
想学会Python自动化办公?这20个Excel表格操作脚本一定要掌握!
前端·后端·python
ALe要立志成为web糕手21 分钟前
用Python实现TCP代理
网络·python·网络协议·tcp/ip·安全·web安全