#7二叉树终止条件&参数向上传递

617. 合并二叉树

错误点:只在递归函数dfs之前创建了一个节点node,整个递归过程只用同一个全局节点对象,则最后的node值为同一个(最后递归的结果),应该在递归函数里面创建node,使得每次递归都有一个新的node,因为这是一个新的树。

'错误做法:'
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        node = TreeNode(0)
        def dfs(node1, node2):
            if node1 is None:
                return node2
            if node2 is None:
                return node1
            node.val = node1.val + node2.val
            #node = TreeNode(node1.val + node2.val)          
            node.left = dfs(node1.left,node2.left)
            node.right = dfs(node1.right,node2.right)
            return node

终止条件:

原则:根据问题需求选择最小且必要的终止条件,确保覆盖所有边界情况,

 空节点检查(最常见)
if root is None:
    return None  '或其他默认值'

叶子节点检查(针对叶子节点执行特定操作,提前终止递归。

if root.left is None and root.right is None:
    # 处理叶子节点的逻辑
    return root
   '或其他返回值return 1'

特定值或条件检查(在满足特定条件时立即返回结果,减少不必要的递归。

if root.val == target:
    return root  # 找到目标节点

多树同步终止(简化空树处理逻辑

"处理逻辑1:直接复用非空树节点"
if tree1 is None:
    return tree2  # 当一棵树为空时,直接返回另一棵树


  "处理逻辑2:每个节点都创建一个node复杂版"
    if root1 is None and root2 is None:
        return None  # 两节点都为空
    if root1 is None:
        return TreeNode(root2.val)  # 仅root2存在,创建新节点
    if root2 is None:
        return TreeNode(root1.val)  # 仅root1存在,创建新节点

#7二叉树终止条件&参数向上传递_第1张图片

示例:

  1. 空节点检查:更通用,代码更简洁,适用于所有树结构。
  2. 叶子点检查:多了一层条件判断,但在某些场景下可减少递归深度(如叶子节点集中的树)
def maxDepth(root):
    if root is None:  # 空节点深度为0
        return 0
    left_depth = maxDepth(root.left)
    right_depth = maxDepth(root.right)
    return max(left_depth, right_depth) + 1


def maxDepth(root):
    if root is None:  # 处理空树
        return 0
    if root.left is None and root.right is None:  # 叶子节点深度为1
        return 1
    left_depth = maxDepth(root.left)
    right_depth = maxDepth(root.right)
    return max(left_depth, right_depth) + 1

700. 二叉搜索树中的搜索

'错误的:'
def dfs(node,val):
            if node is None:
                return
            if node.val == val:
                return node
             '没有保存或者传递left / right子树找到结果后,这个结果如何传递给上层node节点,是断开的'
            dfs(node.left,val)
            dfs(node.right,val)



'正确的:'
def dfs(node,val):
            if node is None:
                return
            if node.val == val:
                return node
            '左边left值保留在node同一层,即传递给node当层的值'
            left = dfs(node.left,val)
            if left:
                return left
            return dfs(node.right,val)
        return dfs(root,val)

是否需要向上传递:

核心原则:是否传递结果取决于「父节点的操作是否依赖子树的返回值」,而与遍历顺序无关。

一、不需要传递结果的递归场景:

1. 纯操作型递归(无返回值

执行某种操作(如遍历修改节点),而非计算搜索时,无需传递结果。子节点不需要被父节点使用,每个节点相互独立。父节点无需等待子树的「返回结果」。

def preorder_traversal(root):
    if root is None:
        return
    print(root.val)  # 操作:打印当前节点
    preorder_traversal(root.left)  # 递归左子树,无需返回值
    preorder_traversal(root.right)  # 递归右子树,无需返回值

2. 副作用记录结果(通过可变对象):

通过修改全局变量或传入的可变对象[]来隐式记录结果,无需显式返回。函数没有return语句,直接修改传入的result列表。

def collect_leaves(root, leaves=None):
    if leaves is None:
        leaves = []
    if root is None:
        return
    if root.left is None and root.right is None:
        leaves.append(root.val)  # 副作用:修改列表
    collect_leaves(root.left, leaves)
    collect_leaves(root.right, leaves)
    # 无需返回leaves,因为列表是可变对象,已被修改

二、需要传递结果的递归场景:

1. 计算型递归(需要汇总子结果)

父节点的深度需要基于左右子树的深度计算 ,通过return向上传递。

def tree_depth(root):
    if root is None:
        return 0
    left_depth = tree_depth(root.left)  # 传递左子树深度
    right_depth = tree_depth(root.right)  # 传递右子树深度
    return max(left_depth, right_depth) + 1  # 汇总子结果

2. 搜索型递归(需要向上传递找到的目标)

虽然遍历顺序是前序,但本质是「搜索」而非纯遍历,需要将子树中找到的目标节点向上传递。不加return就是遍历整棵树了。

def search_node(root, target):
    if root is None:
        return None
    if root.val == target:
        return root  # 找到目标,传递给父节点
    left_result = search_node(root.left, target)  # 传递左子树结果
    if left_result:
        return left_result
    return search_node(root.right, target)  # 传递右子树结果

98. 验证二叉搜索树

前序遍历:

  1. 一般初始是有输入一个值,和根节点比较;检查根节点,检查左右节点子树的性质。
  2. 遍历顺序和处理顺序一致,遍历同时返回bool值。
def isValidBST(self, root: Optional[TreeNode], left=-inf, right=inf) -> bool:
        if not root:
            return True
        x = root.val
        '检查根节点值bool'
        if x >= right or x <= left:
            return False
        '检查左右子树bool'
        return self.isValidBST(root.left,left,x) and self.isValidBST(root.right,x,right)
        

 后序遍历:

  1. 先遍历,遍历获得左子树,右子树的返回值存储起来
  2. 和节点进行比较,不管符合条件或者不符合条件,结果都向有return返回;
def isValidBST(self, root: Optional[TreeNode]) -> bool:
    def dfs(node):
            if not node:
                return inf,-inf
            l_min,l_max = dfs(node.left)
            r_min,r_max = dfs(node.right)
            x = node.val
            '不符合条件,返回false(-inf,inf)'
            if x <= l_max or x >= r_min:
                return -inf,inf
            '符合条件,确保每层都会有向上的返回值'
            return min(l_min,x),max(r_max,x)

     return dfs(root)[0] != -inf

530. 二叉搜索树的最小绝对差

二叉搜索树的顺序性,使用中序遍历更方便。

def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        '记录更新遍历过的最小值'
        ans = float('inf')
        '指针记录当前遍历的节点值'
        pre = float('inf')
        def dfs(node):
            if node is None:
                return
            dfs(node.left)
            nonlocal ans, pre
            '记录最小值'
            ans = min(abs(node.val - pre),ans)
            '更新当前节点值'
            pre = node.val
            dfs(node.right)
            return ans
        return dfs(root)

501. 二叉搜索树中的众数

def findMode(self, root: Optional[TreeNode]) -> List[int]:
        ans = []  '记录结果的数组'
        pre = None  '指针'
        current_count = 0 '当前值频率统计'
        Max_count = 0   '最大频率值'

        def dfs(node):
            if not node:
                return
            nonlocal pre, ans, Max_count, current_count
            dfs(node.left)
            if node.val == pre:
                current_count += 1
            else:
                current_count = 1    
            if current_count == Max_count:
                ans.append(node.val)
            elif current_count > Max_count:
                ans = [node.val]
            Max_count = max(current_count,Max_count)
            pre = node.val
            dfs(node.right)
        
        dfs(root)
        return ans

236. 二叉树的最近公共祖先

二叉搜索树LCA

  1. p, q一定都在树里面,
  2. 可以预先判断p, q是在左树还是右树,有目的遍历,确认在左树或右树,一定不会遍历到空值,就可返回。
  3. 所以不需要加空值的终止判断

二叉树LCA

  1. p, q一定都在树里面,但不知道是在左树还是右树,所以左右都要遍历,
  2. 若p, q在一边的树,另一棵树会遍历到空值,并且找不到,
  3. 所以需要加空值的终止判断。

这部分代码:

  • 虽然代码在递归前检查了根节点的终止条件,但真正的核心逻辑决策这部分属于后序的顺序遍历,核心决策(判断当前节点是否为 LCA)发生在递归遍历左右子树之后。只有获取了左右子树的返回值,才能确定当前节点是否为 LCA。
  • 终止条件,用于快速返回目标节点或空节点,避免无效递归。
  • 后序遍历确保了从叶子节点到根节点的信息传递,适用于需要子树全局信息的问题。
def lowestCommonAncestor(self, root, p, q):
    '终止条件(根节点的早期检查,但非核心处理)'
    if not root or root is q or root is p:
        return root
    
    '递归遍历左子树(获取左子树的结果)'
    left = self.lowestCommonAncestor(root.left, p, q)
    
    '递归遍历右子树(获取右子树的结果)'
    right = self.lowestCommonAncestor(root.right, p, q)
    
    '合并左右子树的结果(根节点的核心处理)'
    if not left and not right:
        return None
    if not left:
        return right
    if not right:
        return left
    return root  '左右子树各找到一个,当前节点即为LCA'

你可能感兴趣的:(深度优先,算法)