《剑指 Offer》专项突破版 - 面试题 54 : 所有大于或等于节点的值之和(C++ 实现的两种方法)

题目链接LCR 054. 把二叉搜索树转换为累加树 - 力扣(LeetCode)

题目

给定一棵二叉搜索树,请将它的每个节点的值替换成树中大于或等于该节点值的所有节点值之和。假设二叉搜索树中节点的值唯一。例如,输入下图 (a) 所示的二叉搜索树,由于有两个节点的值大于或等于 6(即节点 6 和节点 7),因此值为 6 的节点的值替换成 13,其他节点的值的替换过程与此类似,所有节点的值替换之后如下图 (b) 所示。

分析

首先需要注意到这个题目与节点值的大小顺序相关,因为要找出比某节点的值大的所有节点。在二叉搜索树的常用遍历算法中,只有中序遍历是按照节点值递增的顺序遍历所有节点的。

通常,二叉搜索树的中序遍历按照节点的值从小到大的顺序遍历,也就是当遍历到某个节点时比该节点的值小的节点都已经遍历过,因此也就知道了所有比该节点的值小的所有节点的值之和 sum。可是题目要求把每个节点的值替换成大于或等于该节点的值的所有节点的值之和。因此,可以先遍历一遍二叉树求出所有节点的值之和 total,再用 total 减去 sum 即可

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        int total = 0;
        inOrder(root, total);
​
        stack<TreeNode*> st;
        int sum = 0;
        TreeNode* cur = root;
        while (cur || !st.empty())
        {
            while (cur)
            {
                st.push(cur);
                cur = cur->left;
            }
​
            cur = st.top();
            st.pop();
            int tmp = cur->val;
            cur->val = total - sum;
            sum += tmp;
            cur = cur->right;
        }
        return root;
    }
private:
    void inOrder(TreeNode* root, int& total)
    {
        if (root == nullptr)
            return;
        
        inOrder(root->left, total);
        total += root->val;
        inOrder(root->right, total);
    }
};

上面的思路需要遍历二叉搜索树两次,第 1 次不管用什么算法只要遍历所有节点即可,第 2 次则必须采用中序遍历。是否可以只遍历二叉树一次呢

如果能够按照节点值从大到小顺序遍历二叉搜索树,那么只需要遍历一次就够了,因为遍历到一个节点之前大于该节点的值的所有节点已经遍历过了。通常得中序遍历是先遍历左子树,再遍历根节点,最后遍历右子树,由于左子树节点的值比较小,右子树节点的值比较大,因此总体上就是按照节点的值从小到大遍历的。如果要按照节点的值从大到小遍历,那么只需要改变中序遍历的顺序,先遍历右子树,再遍历根节点,最后遍历左子树,这样遍历的顺序就颠倒过来了

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        stack<TreeNode*> st;
        int sum = 0;
        TreeNode* cur = root;
        while (cur || !st.empty())
        {
            while (cur)
            {
                st.push(cur);
                cur = cur->right;
            }
​
            cur = st.top();
            st.pop();
            sum += cur->val;
            cur->val = sum;
            cur = cur->left;
        }
        return root;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值