leetcode
leetcode 2951 ~ 3000
把二叉搜索树转换为累加树

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

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 31 ms, 内存: 18.2 MB


/*
 * 思路:
 * 1. 我们将树中的节点值存储到一个列表中。
 * 2. 对列表进行降序排序。
 * 3. 使用累加器计算节点值的前缀和。
 * 4. 使用哈希映射存储节点值及其对应的前缀和。
 * 5. 再次遍历树并更新节点值。
 */

import java.util.*;
import java.util.stream.Collectors;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    public TreeNode convertBST(TreeNode root) {
        if (root == null) return null;

        List<Integer> values = new ArrayList<>();
        inOrder(root, values);

        List<Integer> sortedValues = values.stream()
                                            .sorted(Comparator.reverseOrder())
                                            .collect(Collectors.toList());

        Map<Integer, Integer> prefixSumMap = new HashMap<>();
        int sum = 0;
        for (int value : sortedValues) {
            sum += value;
            prefixSumMap.put(value, sum);
        }

        updateValues(root, prefixSumMap);
        return root;
    }

    private void inOrder(TreeNode node, List<Integer> values) {
        if (node != null) {
            inOrder(node.left, values);
            values.add(node.val);
            inOrder(node.right, values);
        }
    }

    private void updateValues(TreeNode node, Map<Integer, Integer> prefixSumMap) {
        if (node != null) {
            updateValues(node.left, prefixSumMap);
            node.val = prefixSumMap.get(node.val);
            updateValues(node.right, prefixSumMap);
        }
    }
}

解释

方法:

这个题解采用了递归的方法来实现二叉搜索树到累加树的转换。首先,定义了一个辅助函数 dfs,它采用反向中序遍历(即先访问右子树,再访问根节点,最后访问左子树)。在访问每个节点时,维护一个累加变量 s,这个变量保存了当前已访问节点的值的总和。当访问到一个节点时,将它的节点值更新为 s 加上该节点原始的值。由于二叉搜索树的特性,这种遍历顺序保证了每个节点可以直接获取到所有大于等于它的节点值的总和。

时间复杂度:

O(n)

空间复杂度:

O(n) in the worst case, O(log n) in the average case

代码细节讲解

🦆
为什么选择反向中序遍历(先右后左)而不是正向中序遍历(先左后右)来实现这个算法?
在此算法中,目的是将每个节点的值更新为原树中所有大于或等于该节点值的所有节点值之和。二叉搜索树的特性是任何节点的右子节点都大于该节点,左子节点都小于该节点。使用反向中序遍历(先访问右子树,再访问节点本身,最后访问左子树)可以确保在处理当前节点之前,已经处理了所有值更大的节点,从而可以直接累加到当前节点的值上。反之,正向中序遍历先处理所有较小的节点,不符合累加树的要求。
🦆
递归实现中使用了nonlocal关键字修改s,如果不使用nonlocal,算法还能正确运行吗?有什么替代的实现方式?
如果不使用nonlocal关键字,s变量将不会在递归调用之间正确传递其值,因为每次递归调用都会尝试在本地作用域内定义一个新的s,从而导致无法累积之前的值。替代的实现方式有两种:一是使用类属性来替代局部变量s,这样s的值就可以在所有实例方法之间共享。二是可以将s作为参数传递给递归函数,并在每次递归调用时返回更新后的s值,从而实现累加和的传递。
🦆
在递归函数中,如果二叉搜索树为null,直接返回None有哪些潜在的问题或者考虑?
在递归函数中,如果二叉搜索树为null,直接返回None是处理递归边界条件的标准方法,它可以避免对null节点进行操作导致错误。从设计和安全性角度来看,这种处理方式可以确保函数的健壮性。在此算法中,返回None主要是为了终止递归,避免对不存在的节点进行进一步的处理,这是正确且必要的。没有特别的潜在问题,只要在递归调用之前正确检查节点是否为null即可。

相关问题