LeetCode力扣题目111:多种算法对比实现二叉树的最小深度

简介: LeetCode力扣题目111:多种算法对比实现二叉树的最小深度

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区:码上找工作

作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

python数据分析可视化:企业实战案例

python源码解读

程序员必备的数学知识与应用

题目描述

给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意:叶子节点是指没有子节点的节点。

示例

示例

输入:

3
   / \
  9  20
    /  \
   15   7

输出:2 (根节点到节点 9 的路径最短)

方法一:递归深度优先搜索(DFS)

解题步骤
  1. 递归终止条件:如果当前节点为空,则返回无穷大(表示没有子节点)。
  2. 递归左右子树:计算左子树和右子树的最小深度。
  3. 计算当前节点的最小深度:当前节点的最小深度为左右子树的最小深度加一。
Python 示例
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
def minDepth(root):
    """
    计算二叉树的最小深度
    :param root: TreeNode, 二叉树的根节点
    :return: int, 最小深度
    """
    if not root:
        return 0
    left = minDepth(root.left)
    right = minDepth(root.right)
    # 如果左或右子树为空,应返回非空子树的深度
    if not root.left or not root.right:
        return max(left, right) + 1
    return min(left, right) + 1
算法分析
  • 时间复杂度:(O(n)),每个节点访问一次。
  • 空间复杂度:(O(h)),递归栈的深度,其中 (h) 是树的高度。
    方法一的基本思路是使用深度优先搜索(DFS)递归地检查每个节点的左右子树的最小深度。虽然这种方法直观易懂,但存在重复计算和不必要的深度遍历问题,尤其是在遇到高度不平衡的树时。我们可以通过一些改进来优化这种方法。

方法一改进:优化的DFS

改进点
  1. 提前终止:在发现当前节点的一个子树深度已经小于另一个子树时,可以提前终止对该较深子树的深度计算。这样做可以减少不必要的递归调用。
  2. 缓存结果:对于每个节点的左右子树深度,可以使用哈希表或数组缓存其结果,避免重复计算。
Python 示例
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
def minDepth(root):
    from functools import lru_cache
    
    @lru_cache(None)  # 缓存节点深度计算结果
    def depth(node):
        if not node:
            return float('inf')  # 空节点返回无穷大,表示不可达
        if not node.left and not node.right:
            return 1  # 叶子节点深度为1
        # 使用缓存结果,避免重复计算
        left_depth = depth(node.left)
        right_depth = depth(node.right)
        # 提前终止,如果一个子树深度明显小于另一个,不继续计算较大深度子树
        return min(left_depth, right_depth) + 1
    if not root:
        return 0
    return depth(root)
算法分析
  • 时间复杂度:通过缓存优化后,每个节点最多被计算一次,因此时间复杂度为 (O(n))。
  • 空间复杂度:因为增加了缓存,所以空间复杂度可能稍高,但在最坏情况下仍然为 (O(h)),其中 (h) 是树的高度,对应于递归栈的最大深度。
优劣势比较
  • 优点
  • 减少了不必要的计算,提高了效率。
  • 通过缓存机制,避免了重复计算相同节点的深度。
  • 缺点
  • 代码复杂度略有增加,需要理解缓存机制。
  • 空间开销可能略大,因为要存储每个节点的计算结果。

通过这种改进,DFS 方法不仅变得更加高效,而且也避免了在不平衡树上的性能陷阱。这使得它更加适用于大规模或深度较大的树结构的场景。

方法二:迭代广度优先搜索(BFS)

解题步骤
  1. 使用队列:利用队列存储每层的节点及其深度。
  2. 层级遍历:遍历每个节点,如果是叶子节点,直接返回其深度。
  3. 更新队列:将节点的子节点入队。
Python 示例
from collections import deque
def minDepth(root):
    if not root:
        return 0
    queue = deque([(root, 1)])  # 存储节点及其深度
    while queue:
        node, depth = queue.popleft()
        if not node.left and not node.right:
            return depth
        if node.left:
            queue.append((node.left, depth + 1))
        if node.right:
            queue.append((node.right, depth + 1))
算法分析
  • 时间复杂度:(O(n)),每个节点至多访问一次。
  • 空间复杂度:(O(n)),在最坏的情况下,队列中需要存储所有节点。

方法二使用的是广度优先搜索(BFS)来确定二叉树的最小深度。它通过迭代方式检查每一层的节点,直到找到第一个叶子节点,然后立即返回这个叶子节点的深度。这个方法的主要优点是它不必检查所有的节点,尤其是在一个高度不平衡的树中,它可以更快地找到最浅的叶子节点。尽管如此,我们仍然可以对其进行一些改进,以提高其效率和可用性。

方法二改进:优化的BFS

改进点
  1. 避免使用额外的深度存储:在当前的实现中,每个节点及其对应的深度都存储在队列中。我们可以优化这一点,通过在每一轮循环开始时记录队列的长度,从而避免存储每个节点的深度。
  2. 更早的终止条件:在找到第一个叶子节点后,可以立即退出循环,而不是等待当前层的所有节点都被检查完。
Python 示例
from collections import deque
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
def minDepth(root):
    if not root:
        return 0
    queue = deque([root])
    depth = 0  # 初始化深度为0
    while queue:
        depth += 1  # 开始新的一层,深度加1
        for _ in range(len(queue)):  # 遍历当前层的所有节点
            node = queue.popleft()
            if not node.left and not node.right:  # 找到第一个叶子节点
                return depth
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
    return depth  # 在所有节点都有子节点的情况下返回最终深度
算法分析
  • 时间复杂度:在最坏情况下,即遍历到最后一层才找到叶子节点,时间复杂度仍为 (O(n))。
  • 空间复杂度:空间复杂度主要取决于队列中存储的节点数,最坏情况下,队列中可能包含 (n/2) 个节点(最后一层的节点数),因此空间复杂度为 (O(n))。
优劣势比较
  • 优点
  • 立即找到叶子节点后就结束,避免了不必要的计算。
  • 不需要额外存储节点深度,简化了代码。
  • 缺点
  • 在极端情况下(例如,当树高度非常大时),空间复杂度可能仍然较高。

通过这种改进,BFS 方法更加高效和直观,尤其是在处理大型数据集时,这种方法能快速找到最小深度,而无需深入遍历树的所有部分。这使得它在实际应用中更加实用,尤其是在数据结构动态变化较大的环境中。

应用示例

这些方法在需要快速确定数据结构(如游戏、网络路由、社交网络的层级结构)中的最小路径或深度时非常有用。

欢迎关注微信公众号 数据分析螺丝钉


相关文章
|
7天前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
11天前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
32 5
|
11天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
14天前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
24 0
|
1月前
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
23 2
|
1月前
|
程序员 C语言
【C语言】LeetCode(力扣)上经典题目
【C语言】LeetCode(力扣)上经典题目
|
1月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
20 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
1月前
|
存储 算法 搜索推荐
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
这篇文章主要介绍了顺序存储二叉树和线索化二叉树的概念、特点、实现方式以及应用场景。
21 0
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
113 2