LeetCode题目112:多种算法实现路径总与改进过程

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例
python源码解读
程序员必备的数学知识与应用

题目描述

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

示例

示例

输入:

    5
   / \
  4   8
 /   / \
11  13  4
/ \      \
7   2      1

目标和 = 22

输出:True(存在路径 5 -> 4 -> 11 -> 2,和为 22)

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

解题步骤
  1. 递归函数定义:定义一个辅助函数来递归检查每个节点,传递当前的累计总和。
  2. 递归终止条件:如果当前节点是叶子节点,并且其值加上累计总和等于目标值,返回 True
  3. 递归逻辑:递归检查当前节点的左右子节点。
Python 示例
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def hasPathSum(root, sum):
    """
    检查二叉树中是否存在根到叶子的路径总和等于给定值
    :param root: TreeNode, 二叉树的根节点
    :param sum: int, 目标和
    :return: bool
    """
    if not root:
        return False
    sum -= root.val
    if not root.left and not root.right:  # 检查是否为叶子节点
        return sum == 0
    return hasPathSum(root.left, sum) or hasPathSum(root.right, sum)
算法分析
  • 时间复杂度:(O(n)),每个节点访问一次。
  • 空间复杂度:(O(h)),递归栈的深度,其中 (h) 是树的高度。

方法一使用递归深度优先搜索(DFS)来寻找是否存在从根到叶子的路径,其和等于给定的目标值。虽然这种方法直观且易于实现,但在某些情况下可能会进行不必要的递归调用,尤其是在不平衡的树中。因此,我们可以通过一些技术改进它,以提高效率和实用性。

方法一改进:优化的递归DFS

改进点
  1. 短路逻辑:在递归过程中,一旦找到符合条件的路径,即可立即停止进一步的递归,避免不必要的计算。
  2. 减少参数传递:可以通过直接修改传递的 sum 参数来减少参数的数量,这样可以稍微降低递归调用的开销。
改进后的 Python 示例
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def hasPathSum(root, sum):
    """
    使用优化的DFS检查是否存在从根到叶的路径总和等于给定值
    :param root: TreeNode, 二叉树的根节点
    :param sum: int, 目标和
    :return: bool
    """
    if not root:
        return False

    # 检查是否到达叶子节点并且路径总和匹配
    if not root.left and not root.right:
        return root.val == sum

    # 递归检查左右子树
    sum -= root.val
    return hasPathSum(root.left, sum) or hasPathSum(root.right, sum)

# 示例调用
root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2)), None), TreeNode(8, TreeNode(13), TreeNode(4, None, TreeNode(1))))
print(hasPathSum(root, 22))  # 输出: True
算法分析
  • 时间复杂度:改进后,时间复杂度依然为 (O(n)),因为每个节点仍然需要至多被访问一次。
  • 空间复杂度:改进后,空间复杂度也为 (O(h)),其中 (h) 是树的高度,对应于递归栈的最大深度。通过短路逻辑,可能在找到第一个有效路径时减少空间使用。
优劣势比较
  • 优点
    • 短路逻辑可以减少在找到有效路径后的不必要递归,提高效率。
    • 直接修改 sum 减少了参数传递,稍微优化了递归的性能。
  • 缺点
    • 递归深度依然依赖于树的高度,对于极端不平衡的树依然可能导致较高的空间复杂度。

这种改进使得递归方法在实际应用中更加高效,尤其是在早期找到符合条件的路径时,能够显著减少计算量。

方法二:迭代使用栈

解题步骤
  1. 使用栈:利用栈存储每个节点及其对应的路径总和。
  2. 迭代逻辑:迭代访问每个节点,更新路径总和,检查叶子节点的路径总和。
Python 示例
def hasPathSum(root, sum):
    if not root:
        return False
    stack = [(root, root.val)]
    while stack:
        node, curr_sum = stack.pop()
        if not node.left and not node.right and curr_sum == sum:
            return True
        if node.right:
            stack.append((node.right, curr_sum + node.right.val))
        if node.left:
            stack.append((node.left, curr_sum + node.left.val))
    return False
算法分析
  • 时间复杂度:(O(n)),每个节点最多被访问一次。
  • 空间复杂度:(O(n)),在最坏的情况下,栈中需要存储所有节点。

方法二使用迭代的方式通过栈实现深度优先搜索(DFS),检查是否存在从根到叶的路径,其和等于给定目标值。虽然迭代方法减少了递归可能引起的栈溢出问题,但其管理栈的方式仍有改进空间,特别是在如何更高效地处理节点和路径和的记录。

方法二改进:优化的迭代DFS

改进点
  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

class PathNode:
    def __init__(self, node, current_sum):
        self.node = node
        self.current_sum = current_sum

def hasPathSum(root, targetSum):
    if not root:
        return False

    stack = deque([PathNode(root, root.val)])  # 使用PathNode来存储节点和当前路径和

    while stack:
        path_node = stack.pop()
        node = path_node.node
        current_sum = path_node.current_sum

        # 检查是否到达叶子节点且路径和符合条件
        if not node.left and not node.right and current_sum == targetSum:
            return True

        # 将非空子节点和相应的路径和压入栈
        if node.right:
            stack.append(PathNode(node.right, current_sum + node.right.val))
        if node.left:
            stack.append(PathNode(node.left, current_sum + node.left.val))

    return False

# 示例调用
root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2)), None), TreeNode(8, TreeNode(13), TreeNode(4, None, TreeNode(1))))
print(hasPathSum(root, 22))  # 输出: True
算法分析
  • 时间复杂度:优化后,时间复杂度仍为 (O(n)),每个节点最多被访问一次。
  • 空间复杂度:优化后,空间复杂度也为 (O(n)),在最坏的情况下可能需要存储所有节点。
优劣势比较
  • 优点
    • 代码结构更清晰,通过使用 PathNode 类减少了代码的复杂度和出错概率。
    • 早停机制提高了效率,避免了不必要的迭代。
  • 缺点
    • 空间复杂度相比递归方法有所提高,因为迭代方法需要显式使用栈存储节点。

通过这种改进,迭代方法在处理大型数据集时更加健壮且易于维护,尤其适用于深度较大的树结构,减少了递归可能导致的栈溢出风险。

应用示例

这些方法在计算机科学中广泛应用于路径查找、网络流量分析、资源管理等领域,其中需要确定从一个点到另一个点的可达性或路径成本。

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据分析螺丝钉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值