Tree的基础算法

210 阅读13分钟

结构转换

BST的重要性质:

  • BST的中序遍历结果是一个sorted array BST的2个解题技巧:
  • DFS时不断更新前驱节点prev,满足prev.val < cur.val
  • 传递BST的上下界low, high,控制BST的构建/验证

428. 序列化和反序列化 N 叉树(Hard)

image.png

Solu:

  • root.val之后,加入一位标识len(root.children),以确定需要loop多少次deserialize来构建所有children
  • 为了只在数字之间添加',',可以先把所有数字装进一个list,再转化为str

Code:

class Codec:
    def serialize(self, root: 'Node') -> str:
        def dfs(node):
            if node:
                res.append(str(node.val))
                res.append(str(len(node.children)))
                for child in node.children:
                    dfs(child)
        
        res = []
        dfs(root)
        return ','.join(res) if res else ''
    
    def deserialize(self, data: str) -> 'Node':
        def dfs(list_data):
            root_val = list_data.pop(0)
            root = Node(root_val, [])
            num_children = int(list_data.pop(0))
            for _ in range(num_children):
                root.children.append(dfs(list_data))
            return root
        
        if not data:
            return None
        return dfs(data.split(','))


449. 序列化和反序列化二叉搜索树(Medium)

image.png

Solu:

  • 因为题目要求编码的字符串应尽可能紧凑,所以不可以用'#'来标识null节点
  • 一旦list[0]超出边界,则说明当前正在构建的BST子树已经构建完了,可以去构建下一个BST子树

Code:

class Codec:
    
    def serialize(self, root: TreeNode) -> str:
        res = '' if root is None else str(root.val)
        if not root:
            return res
        if root.left:
            res += ',' + self.serialize(root.left)
        if root.right:
            res += ',' + self.serialize(root.right)
        return res
    
    def deserialize(self, data: str) -> TreeNode:
        def dfs(list_data, low, high):
            if not list_data:
                return None
            val = list_data[0]
            if int(val) < low or int(val) > high:
                return None
            node = TreeNode(int(list_data.pop(0)))
            node.left = dfs(list_data, low, int(val))
            node.right = dfs(list_data, int(val), high)
            return node
        
        return dfs(data.split(','), -sys.maxsize, sys.maxsize) if data else None


1008. 前序遍历构造二叉搜索树(Medium)

image.png

Solu:

  • 上下边界lowhigh控制:一旦preorder[0]超界,则说明当前BST子树构建完了,可以去构建下一棵BST子树

Code:

class Solution:
    def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        def dfs(low, high):
            if not preorder or preorder[0] < low or preorder[0] > high:
                return None
            root = TreeNode(preorder.pop(0))
            root.left = dfs(low, root.val)
            root.right = dfs(root.val, high)
            return root
        
        return dfs(float('-inf'), float('inf'))


426. 将二叉搜索树转化为排序的双向链表(Medium)

image.png

Solu:

  • 双端链表的head节点是BST的最左leaf node
  • DFS中序遍历时,不断更新前驱prev,将prevcurNode做链接
  • 勿忘:将双端列表的首尾链接

Code:

class Solution:
    def __init__(self):
        self.head = None  # 双端链表的头节点为tree的最左leaf node
        self.prev = None  # 记录前驱节点
    
    def treeToDoublyList(self, root: 'Optional[Node]') -> 'Optional[Node]':
        def dfs(node):  # in-order traversal
            if not node:
                return
            dfs(node.left)
            if not self.head:
                self.head = node
            if self.prev:
                self.prev.right = node
                node.left = self.prev
            self.prev = node
            dfs(node.right)
        
        dfs(root)
        if self.head:  # 调整双端链表的首尾
            self.head.left = self.prev
            self.prev.right = self.head
        return self.head


BST

270. 最接近的二叉搜索树值(Easy)

image.png

Solu:

  • if target == root.val, then ans = root必然成立
  • if target < root.val, then ans = root(上界) OR 左子树中最接近target的node(下界)
  • if target > root.val, then ans = root(下界) OR 右子树中最接近target的node(上界)

Code:

class Solution:
    def closestValue(self, root: Optional[TreeNode], target: float) -> int:
        if not root:
            return sys.maxsize
        if root.val == target:
            return root.val
        if target > root.val:
            right = self.closestValue(root.right, target)
            return right if abs(right - target) < abs(root.val - target) else root.val
        else:
            left = self.closestValue(root.left, target)
            return left if abs(left - target) < abs(root.val - target) else root.val


450. 删除二叉搜索树中的节点(Medium)

Solu:

  • 对于待删除的node,用该node的 左子树的最大值 OR 右子树的最小值 来填补

Code:

class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        def findMin(node) -> int:
            while node.left:
                node = node.left
            return node.val
        
        if not root:
            return None
        if root.val < key:
            root.right = self.deleteNode(root.right, key)
        elif root.val > key:
            root.left = self.deleteNode(root.left, key)
        else:
            if not root.left:
                return root.right
            if not root.right:
                return root.left
            root.val = findMin(root.right)  # replace root with min value in right subtree
            root.right = self.deleteNode(root.right, root.val)  # delete min value in right subtree
        return root


98. 验证二叉搜索树(Medium)

Solu 1:传入上下边界

  • if root.val <= lower or root.val >= upper,则验证失败;否则,再分别验证左右子树

Code 1:

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        def recursion(lower, upper, node):
            if node is None:
                return True
            val = node.val
            if val <= lower or val >= upper:
                return False
            return recursion(lower, val, node.left) and recursion(val, upper, node.right)
        
        return recursion(float('-inf'), float('inf'), root)

Solu 2:中序遍历 + 记录前驱prev

  • 中序遍历,结果是sorted array
  • 验证的同时,不断更新prev,一旦prev.val >= cur.val,则验证失败
class Solution:
    def __init__(self):
        self.prev = None
    
    def isValidBST(self, root: TreeNode) -> bool:
        def dfs(node):
            if not node:
                return True
            if not dfs(node.left):
                return False
            if self.prev and self.prev.val >= node.val:
                return False
            self.prev = node
            return dfs(node.right)
        
        return dfs(root)


173. 二叉搜索树迭代器(Meidum)

image.png

Solu:stack迭代式中序遍历

  • 利用iterative的形式(stack)对BST进行中序遍历

image.png

Code:

class BSTIterator:
    
    def __init__(self, root: TreeNode):
        self.stack = []
        self.pushAllLeft(root)
    
    def next(self) -> int:
        node = self.stack.pop()
        self.pushAllLeft(node.right)
        return node.val
    
    def hasNext(self) -> bool:
        return len(self.stack) > 0
    
    def pushAllLeft(self, root):
        while root:
            self.stack.append(root)
            root = root.left


99. 恢复二叉搜索树(Medium)

image.png

Solu:

  • 只需要按照inorder去遍历,发现2个顺序出问题的node交换即可
    • 只保留第一次出现的first最后一次出现的second,并对其进行swap

Code:

class Solution:
    def __init__(self):
        self.prev = TreeNode(float('-inf'))
        self.first = None
        self.second = None
    
    def recoverTree(self, root: Optional[TreeNode]) -> None:
        def inorder(node) -> None:
            if not node:
                return
            inorder(node.left)
            if self.prev.val >= node.val:
                if not self.first:
                    self.first = self.prev
                self.second = node
            self.prev = node
            inorder(node.right)
        
        inorder(root)
        self.first.val, self.second.val = self.second.val, self.first.val


Solu:

LC108 的套娃题

  • 中序遍历得到排序
  • 按照排序从中间开始构建树,左右平衡,深度差异不会大于1

Code:

class Solution:
    def balanceBST(self, root: TreeNode) -> TreeNode:
        def inorder(node, res):
            if node:
                inorder(node.left, res)
                res.append(node.val)
                inorder(node.right, res)
        
        def construct(res, l, r):
            if l > r:
                return None
            mid = (l + r) // 2
            node = TreeNode(res[mid])
            node.left = construct(res, l, mid - 1)
            node.right = construct(res, mid + 1, r)
            return node
        
        res = []
        inorder(root, res)
        return construct(res, 0, len(res) - 1)


96. 不同的二叉搜索树(Medium)

image.png

Solu 1:记忆化DFS

image.png

  • 一个BST有多少种排序方式只和 左右子树各自的#nodes 相关(和node的val无关)
  • numTrees(n) = sum(numsTrees(i-1) * numTrees(n-i)) (1 ≤ i ≤ n)

Code 1:

class Solution:
    def __init__(self):
        self.memo = {}
    
    def numTrees(self, n: int) -> int:
        if n <= 1:
            return 1
        if n in self.memo:
            return self.memo[n]
        res = 0
        for i in range(1, n + 1):
            res += self.numTrees(i - 1) * self.numTrees(n - i)
        self.memo[n] = res
        return res

Solu 2:DP

  • dp[i] = i个nodes可以组成多少种不同的BST
  • dp[i] = sum(dp[k-1] * dp[i-k]) (1 ≤ k ≤ i)

Code 2:

class Solution:
    def numTrees(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[0] = 1
        dp[1] = 1
        for i in range(2, n + 1):
            for j in range(1, i + 1):
                dp[i] += dp[j - 1] * dp[i - j]
        return dp[n]


95. 不同的二叉搜索树 II(Medium)

image.png

Solu:DFS暴力枚举

  • dfs(start, end) = 用node start ~ end(闭区间)可以生成哪些BST

Code:

class Solution:
    def generateTrees(self, n: int) -> List[TreeNode]:
        def dfs(start, end):
            if start > end:
                return [None]
            if start == end:
                return [TreeNode(start)]
            res = []
            for i in range(start, end + 1):
                left = dfs(start, i - 1)
                right = dfs(i + 1, end)
                for l in left:
                    for r in right:
                        root = TreeNode(i, l, r)
                        res.append(root)
            return res
        
        return dfs(1, n) if n else []


LCA

本质上是:

  1. 拿到所有childNode的处理结果
  2. 在当前节点汇总来自所有childNode的数据,整理出一个新的答案(data自下而上

❤️ 模版

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root or p == root or q == root:
            return root
        left, right = self.lowestCommonAncestor(root.left, p, q), self.lowestCommonAncestor(root.right, p, q)
        if left and right:  # p, q 分居在左右子树中
            return root
        return left if left else right  # p, q 都在左子树或右子树中

235. 二叉搜索树的最近公共祖先(Easy)

image.png

Solu:

  • 如果pq中一个落在root上 或者 p,q分居root两侧,那么root即为LCA
  • 否则,在p,q所在的那一侧subtree上递归搜索

Code:

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if p.val > q.val:  # 易于比较,保证p是val更小的那个node
            p, q = q, p
        if root.val == p.val or root.val == q.val or p.val < root.val < q.val:
            return root
        elif root.val < p.val:
            return self.lowestCommonAncestor(root.right, p, q)
        else:
            return self.lowestCommonAncestor(root.left, p, q)


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

image.png

Solu 1:

照抄模版

  • if lowestCommonAncestor(root, p, q) is not None,then 表明在以root为根节点的tree上找到了p或者q
    • if root == p or root == q, 表明LCA必定是root
    • if left and right, then 表明p,q分居root两侧
    • if left 或者 if right, 表明p,q同时都在root的左/右子树

Code 1:

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root or p == root or q == root:
            return root
        left, right = self.lowestCommonAncestor(root.left, p, q), self.lowestCommonAncestor(root.right, p, q)
        if left and right:  # p, q 分居在左右子树中
            return root
        return left if left else right  # p, q 都在左子树或右子树中

Solu 2:Map

  • memo记录每个node的最近祖先(即:parent)
  • p,q各自通往root的path第一次产生交集,则说明找到了它们的LCA

Code 2:

class Solution:
    def __init__(self):
        self.memo = {}
    
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def dfs(node, parent) -> None:
            if not node:
                return
            self.memo[node] = parent
            dfs(node.left, node)
            dfs(node.right, node)
        
        dfs(root, None)
        seen = set()
        while p or q:
            if p and p in seen:  # 产生交集
                return p
            if p:
                seen.add(p)
                p = self.memo[p]
            if q and q in seen:  # 产生交集
                return q
            if q:
                seen.add(q)
                q = self.memo[q]
        return None


1644. 二叉树的最近公共祖先 II(Medium)

image.png

Solu 1:

  • 对模版做修改:改成postOrder,强制遍历所有nodes

Code 1:

class Solution:
    def __init__(self):
        self.seen = 0
    
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def dfs(node):
            if not node:
                return node
            left = dfs(node.left)
            right = dfs(node.right)
            if node.val == p.val or node.val == q.val:
                self.seen += 1
                return node
            if left and right:
                return node
            return left if left else right
        
        ans = dfs(root)
        return ans if self.seen == 2 else None

Solu 2:Map

  • memo记录每个node的最近祖先(也就是parent)
  • 一旦p,q各自通往root的path产生了交集,则说明找到了LCA - 根据题意,要先check p,q是否都在tree中

Code 2:

class Solution:
    def __init__(self):
        self.memo = {}
    
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def dfs(node, parent) -> None:
            if not node:
                return
            self.memo[node] = parent
            dfs(node.left, node)
            dfs(node.right, node)
        
        dfs(root, None)
        seen = set()
        if p in self.memo and q in self.memo:
            while p or q:
                if p and p in seen:  # 产生交集,找到LCA
                    return p
                if p:
                    seen.add(p)
                    p = self.memo[p]
                if q and q in seen:  # 产生交集,找到LCA
                    return q
                if q:
                    seen.add(q)
                    q = self.memo[q]
        return None


1650. 二叉树的最近公共祖先 III(Medium)

image.png

Solu:“一路向北”

  • pq都同时往root进发,一旦两条paths产生交集,则说明找到了LCA

Code:

class Solution:
    def __init__(self):
        self.seen = set()
    
    def lowestCommonAncestor(self, p: 'Node', q: 'Node') -> 'Node':
        while p or q:
            if p and p in self.seen:
                return p
            if p:
                self.seen.add(p)
                p = p.parent
            if q and q in self.seen:
                return q
            if q:
                self.seen.add(q)
                q = q.parent
        return None


1676. 二叉树的最近公共祖先 IV(Medium)

image.png

Solu:

照抄模版,略

Code:

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', nodes: 'List[TreeNode]') -> 'TreeNode':
        if not root or root in nodes:
            return root
        left, right = self.lowestCommonAncestor(root.left, nodes), self.lowestCommonAncestor(root.right, nodes)
        if left and right:
            return root
        return left if left else right


1123. 最深叶节点的最近公共祖先(Medium)

image.png

Solu 1:two-pass

  • pass 1: 先通过BFS找到所有deepest leaf nodes
  • pass 2: 照抄模版,找所有deepest leaf nodes的LCA(参见 LC 1676

Code 1:

class Solution:
    def lcaDeepestLeaves(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        def findDeepestLeaves():
            deepest = []
            q = [root]
            while q:
                deepest = []
                for _ in range(len(q)):
                    node = q.pop(0)
                    deepest.append(node)
                    if node.left:
                        q.append(node.left)
                    if node.right:
                        q.append(node.right)
            return deepest
        
        def LCAMultiNodes(node, targets):
            if not node or node in targets:
                return node
            left = LCAMultiNodes(node.left, targets)
            right = LCAMultiNodes(node.right, targets)
            if left and right:
                return node
            return left if left else right
        
        deepest = findDeepestLeaves()
        return LCAMultiNodes(root, deepest)

Solu 2:one-pass ❤️

  • dfs(node)同时返回
    • node为根节点的tree的最大深度
    • node为根节点的tree的deepest leaf nodes' LCA
  • if 左右子树的最大深度一样,则说明deepest Leaf nodes分居root两侧,LCA = root
  • Otherwise, 所有的deepest leaf nodes都在具有更大最大深度的那一侧,且 那一侧subtree的LCA = 整个tree的LCA

Code 2:

class Solution:
    def lcaDeepestLeaves(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        def dfs(node):
            '''
            同时找到以node为根的树的(最大深度 and 最深叶子结点的LCA)
            :param node:
            :return:
            '''
            if not node:
                return 0, None
            left_height, left_node = dfs(node.left)
            right_height, right_node = dfs(node.right)
            if left_height > right_height:
                return left_height + 1, left_node
            elif left_height < right_height:
                return right_height + 1, right_node
            else:
                return left_height + 1, node
        
        return dfs(root)[1]


信息传递

拿到当前node的信息后,可以自上而下传递给left, right node, 也可以自下而上传递给parent node

  • backtracking / DFS:自上而下
    • 在走一条路不通后,我们undo我们的操作,去下一条路再次尝试
  • pure recursion”:自下而上
    • base case:最小的不可再分的subproblem -> leaf node
    • induction rule:如何利用子问题的结果 -> 当前node处理leftChild, rightChild返回来的结果

backtracking(自上而下)

  • 从某一个节点(不一定是根节点),从上向下寻找路径,到某一个节点(不一定是叶节点)结束
  • 不可以拐弯

模版 ❤️

# 一般路径
def dfs(node, path):
    if not node:
        return
    path.append(node.val)
    if not node.left and not node.right:  # leaf node
        res.ans.append(path[:])
    dfs(node.left, path)
    dfs(node.right, path)
    path.pop()  # backtracking


# 给定sum的路径
def dfs(node, path, sum):
    if not node:
        return
    sum -= node.val
    path.append(node.val)
    if not node.left and not node.right and sum == 0:  # leaf node and sum == 0, add valid path
        res.ans.append(path[:])
    dfs(node.left, path, sum)
    dfs(node.right, path, sum)
    path.pop()  # backtracking

257. 二叉树的所有路径

image.png

Solu:DFS回溯

照抄模版,在leaf node处添加path

Code:

class Solution:
    def __init__(self):
        self.ans = []
    
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def dfs(node, path) -> None:
            if not node:
                return
            path.append(node.val)
            if not node.left and not node.right:
                self.ans.append('->'.join(map(str, path)))
            dfs(node.left, path)
            dfs(node.right, path)
            path.pop()  # backtracking
        
        dfs(root, [])
        return self.ans


1448. 统计二叉树中好节点的数目(Medium)

image.png

Solu:DFS回溯

  • 自上而下传递是一个目前path的最大值,只要curNode >= 传下来的最大值,那么我们就得到了我们的一个答案(good nodes)
  • 一般backtracking写成void函数

Code:

class Solution:
    def __init__(self):
        self.ans = 0
    
    def goodNodes(self, root: TreeNode) -> int:
        def dfs(node, max_val):
            if not node:
                return
            if node.val >= max_val:
                self.ans += 1
            dfs(node.left, max(max_val, node.val))
            dfs(node.right, max(max_val, node.val))
        
        dfs(root, root.val)
        return self.ans


"pure recursion"(自下而上)

从任意节点到任意节点的路径,不需要自顶向下(可以拐弯

模版 ❤️

class Solution:
    def __init__(self):
        self.res = 0
    
    def maxPath(self, root):  # 以root为路径起始点的最长路径
        if not root:
            return 0
        # postOrder强行遍历所有nodes
        left = self.maxPath(root.left)
        right = self.maxPath(root.right)
        self.res = max(self.res, left + right + root.val)  # 穿过root的最长路径,更新res
        return max(left, right) + root.val  # 返回以root为路径起始点的最长路径

基础recursion(返回单一数据)

124. 二叉树中的最大路径和(Hard)

image.png

Solu:

  • maxPath(node):
    • return 以node为起始点的具有最大sum的path
    • 同时更新穿过node的具有最大sum的path(“打擂台”)

Code:

class Solution:
    def __init__(self):
        self.ans = -sys.maxsize
    
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        def maxPath(node) -> int:
            if not node:
                return 0
            left = max(maxPath(node.left), 0)
            right = max(maxPath(node.right), 0)
            self.ans = max(self.ans, node.val + left + right)
            return node.val + max(left, right)
        
        maxPath(root)
        return self.ans


进阶recursion(返回多个同一类型的数据)

1120. 子树的最大平均值(Medium)

image.png

Solu:

image.png

  • 因为需要计算所有subtree的avg,所以对于一个tree:
    • 采用postOrder强行遍历所有的subtree,“打擂台”不断更新ans
    • 同时对于一个node,需要得知其左右子树各自的#nodessumOfTree,才能计算自己这整棵tree的avg

Code:

class Solution:
    def __init__(self):
        self.ans = float('-inf')
    
    def maximumAverageSubtree(self, root: TreeNode) -> float:
        def maxAvg(node):  # 返回以node为根节点tree的[sum, #nodes],同时“打擂台”更新max average
            if not node:
                return 0, 0
            # postOrder
            lsum, lnodes = maxAvg(node.left)
            rsum, rnodes = maxAvg(node.right)
            self.ans = max(self.ans, float(lsum + rsum + node.val) / (lnodes + rnodes + 1))
            return lsum + rsum + node.val, lnodes + rnodes + 1
        
        maxAvg(root)
        return self.ans


1372. 二叉树中的最长交错路径(Medium)

image.png

Solu:

  • dfs(node, isLeft):
    • return 从node出发,以isLeft为起始方向的zigzagPath中有多少个nodes
    • 同时“打擂台”更新从一个节点出发的最长zigzagPath中有多少个nodes

Code:

class Solution:
    def __init__(self):
        self.ans = 0
    
    def longestZigZag(self, root: TreeNode) -> int:
        def dfs(node, isLeft):  # 返回在以node为根节点的tree中,按照指定的方向从root出发得到的zigzagPath有多少个nodes
            if not node:
                return 0
            l = dfs(node.left, False)
            r = dfs(node.right, True)
            self.ans = max(self.ans, max(l, r) + 1)
            return 1 + (l if isLeft else r)
        
        dfs(root, False)
        return self.ans - 1


549. 二叉树中最长的连续序列(Medium)

image.png

Solu: image.png

  • dfs(node):
    • return:
      • incr: 当前以node为根节点的tree中,从node出发的最长连续递增序列的长度
      • decr: 当前以node为根节点的tree中,从node出发的最长连续递减序列的长度
    • 同时“打擂台”,用 穿过node的最长连续序列的长度 更新ans

Code:

class Solution:
    def __init__(self):
        self.ans = 0
    
    def longestConsecutive(self, root: TreeNode) -> int:
        def dfs(node):  # return [incr: node里面的以node为起点的最长连续递增序列长度, decr:以node为起点的最长连续递减序列长度]
            if not node:
                return 0, 0
            lincr, ldecr = dfs(node.left)
            rincr, rdecr = dfs(node.right)
            if node.left and node.left.val == node.val + 1:
                ldecr = 0
            elif node.left and node.left.val == node.val - 1:
                lincr = 0
            else:  # 不连续
                lincr = 0
                ldecr = 0
            if node.right and node.right.val == node.val + 1:
                rdecr = 0
            elif node.right and node.right.val == node.val - 1:
                rincr = 0
            else:  # 不连续
                rincr = 0
                rdecr = 0
            self.ans = max(self.ans, ldecr + rincr + 1, lincr + rdecr + 1)
            return max(lincr, rincr) + 1, max(ldecr, rdecr) + 1
        
        dfs(root)
        return self.ans


其他

❤️ 979. 在二叉树中分配硬币(Medium)

image.png

Solu:

  • 假设node的左儿子已经尽力处理了问题,目前已经走了l_step步,但还是有需求l_demand;相似的,右儿子已经走了r_step步,还有需求r_demand
  • 父节点node为了拯救儿子们,赊账也要搞定。因此自己需要再移动|l_demand| + |r_demand|步;此时儿子们虽然平衡了,但父节点字自己现在的需求变成了node.val + l_demand + r_demand-1
  • 问题在root处被解决,因为本身“家族”内的钱总量是能平衡的,只是分配不均。ans = 一共操作了多少步

Code:

class Solution:
    def distributeCoins(self, root: Optional[TreeNode]) -> int:
        def dfs(node):
            if not node:
                return 0, 0
            l_step, l_demand = dfs(node.left)
            r_step, r_demand = dfs(node.right)
            total_step = l_step + r_step + abs(l_demand) + abs(r_demand)
            total_demand = (l_demand + r_demand + node.val) - 1
            return total_step, total_demand
        
        return dfs(root)[0]


Reference: