LEETCODE 538Medium
把二叉搜索树转换为累加树
反着中序遍历,节点就按从大到小的顺序出现,一个累加变量便能一路加下去。
问题拆解
每个节点要换成“原树中所有大于等于它的值之和”。若对每个节点都去搜集比它大的节点,会做大量重复工作。真正该利用的是二叉搜索树的有序性。
普通中序遍历(左→根→右)会按值从小到大访问节点。而这道题关心的是“比当前值大的部分”,所以把顺序倒过来:右→根→左,节点就会按从大到小的次序出现。
一旦访问顺序是从大到小,“大于等于当前节点的和”就等于“到目前为止累加的总和”,用一个变量顺着记下来即可。
反序中序 + 累加变量
private int total = 0;
public TreeNode convertBST(TreeNode root) {
if (root != null) {
convertBST(root.right);
total += root.val;
root.val = total;
convertBST(root.left);
}
return root;
}
def convertBST(root):
total = 0
def visit(node):
nonlocal total
if not node:
return
visit(node.right)
total += node.val
node.val = total
visit(node.left)
visit(root)
return root
func convertBST(root *TreeNode) *TreeNode {
total := 0
var visit func(node *TreeNode)
visit = func(node *TreeNode) {
if node == nil {
return
}
visit(node.Right)
total += node.Val
node.Val = total
visit(node.Left)
}
visit(root)
return root
}
pub fn convert_bst(root: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
fn visit(node: &Option<Rc<RefCell<TreeNode>>>, total: &mut i32) {
if let Some(n) = node {
let mut n = n.borrow_mut();
visit(&n.right, total);
*total += n.val;
n.val = *total;
visit(&n.left, total);
}
}
let mut total = 0;
visit(&root, &mut total);
root
}
三行遍历顺序是关键:先递归右子树,再处理当前节点,最后递归左子树。处理当前节点时,total 已经累加了所有更大的值,加上自己后写回,就是这个节点的新值。整棵树只需走一遍。
复杂度
| 指标 | 复杂度 | 原因 |
|---|---|---|
| 时间 | O(n) |
反序中序遍历访问每个节点一次 |
| 空间 | O(h) |
递归栈深度等于树高 |
可以迁移的模式
- 二叉搜索树的中序遍历天然有序,正序取小、反序取大,选对方向能省掉排序;
- 遍历过程中携带一个累加/前驱变量,可以把“与其他节点的关系”摊平成一路扫描;
- 当题目问的是“所有比我大/小的元素之和”时,先想能不能用一次有序遍历前缀式地算出来。
顺序遍历配一个滚动状态,是处理有序结构上“前缀 / 后缀聚合”的顺手工具。