把二叉搜索树转换为累加树

标签: 深度优先搜索 二叉搜索树 二叉树

难度: Medium

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意:本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/ 相同

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]
输出:[1,null,1]

示例 3:

输入:root = [1,0,2]
输出:[3,3,2]

示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

提示:

  • 树中的节点数介于 0 和 104 之间。
  • 每个节点的值介于 -104 和 104 之间。
  • 树中的所有值 互不相同
  • 给定的树为二叉搜索树。

Submission

运行时间: 64 ms

内存: 17.4 MB

# 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 convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        s = 0
        def dfs(node):
            nonlocal s
            if node is None:
                return
            dfs(node.right)
            node.val += s
            s = node.val
            dfs(node.left)
        dfs(root)
        return root

Explain

这道题的解法使用了反向中序遍历的思路。在二叉搜索树中,中序遍历可以得到一个递增的序列。而本题要求每个节点的新值等于原树中大于等于该节点值的所有节点值之和,实际上就是反向中序遍历累加的过程。具体来说,我们先遍历右子树,然后处理当前节点,最后遍历左子树。在处理当前节点时,我们将当前的累加和加到该节点的值上,然后更新累加和。这样,在遍历完整棵树后,每个节点的值就变成了大于等于该节点的所有节点值之和。

时间复杂度: O(n)

空间复杂度: O(n)

# 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 convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        s = 0
        def dfs(node):
            nonlocal s
            if node is None:
                return
            # 先遍历右子树
            dfs(node.right)
            # 处理当前节点,将累加和加到当前节点的值上,然后更新累加和 
            node.val += s
            s = node.val
            # 再遍历左子树
            dfs(node.left)
        # 调用反向中序遍历函数
        dfs(root)
        return root

Explore

在二叉搜索树中,中序遍历可以按照递增顺序访问所有节点。对于本题,我们需要计算大于等于每个节点的所有节点值之和。使用反向中序遍历(即先访问右子树,然后访问根节点,最后访问左子树)可以实现从最大值到最小值的顺序访问节点。这样,我们可以持续累加当前及之前访问的节点的值,直接更新每个节点,而无需额外的数据结构来存储中间结果,因此更加高效和直观。

在反向中序遍历中,累加和`s`是用来记录当前节点以及所有已访问的右侧节点的值的总和。当访问到一个节点时,将`s`的当前值加到该节点的值上,然后将更新后的节点值重新赋值给`s`。这样,`s`始终保持为当前节点以及所有右侧节点的值的总和,为之后的节点提供正确的累加基数。

在`dfs`函数中,终止条件是当遍历到的节点为`None`时,函数直接返回并不执行任何操作。这一条件检查发生在函数的开始,确保在尝试访问或修改节点属性(如值或子节点)之前,节点是存在的。这样的检查避免了对空节点的任何操作,保证了代码的安全性和正确性。

在处理完当前节点时立即更新累加和`s`是因为`s`需要在访问左子树之前包含当前节点的值。由于我们是按照“右子树 - 根节点 - 左子树”的顺序进行遍历,当前节点的值在加入累加和后,应立即用于更新左子树中的节点。如果延迟更新`s`,左子树中的节点将无法正确地累加包含当前节点的值,从而导致错误的结果。