【力扣-二叉树】23、把二叉搜索树转换成累加树(538)

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战

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

题目描述

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

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

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

注意: 本题和 1038: leetcode-cn.com/problems/bi… 相同

示例 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 之间。
  • 树中的所有值 互不相同
  • 给定的树为二叉搜索树。

解析

根据给出的示例可以发现,所谓的累加树,其实就是根据中序遍历的顺序,求出每个节点到最后一个节点值的累加和作为当前节点的值。

法1

  • 使用两次中序遍历
    • 1、第一次遍历记录每个元素的值,并且求出最大值
    • 2、第二次遍历修改每个元素的值
      • 规律:当前元素的新值等于前驱节点的新值-前驱节点的旧值
      • 所以:使用三个指针来记录值的变化,
        • 其中preOld指针指向前驱节点的旧值
        • preNew指针指向前驱节点的新值
        • tmp 指针记录当前节点修改前的值

代码

class Solution
{
public:
    TreeNode *convertBST(TreeNode *root)
    {
        // 第一次遍历,求出所有节点值的累加和
        traversal(root);
        // 第二次遍历,更新节点的累加和
        changeVal(root);
        // 返回根节点
        return root;
    }

private:
    int sum;                            // 记录所有节点值的累加和
    TreeNode *preOld = new TreeNode(0); // 记录前驱节点的旧值
    TreeNode *preNew = NULL;            // 记录前驱节点的新值
    TreeNode *tmp = new TreeNode(0);    // 临时节点,记录当前节点的旧值

    // 第一次中序遍历,求节点值累加和
    void traversal(TreeNode *cur)
    {
        if (cur == NULL)
        {
            return;
        }
        // 左
        if (cur->left)
        {
            traversal(cur->left);
        }

        // 中
        sum += cur->val;

        // 右
        if (cur->right)
        {
            traversal(cur->right);
        }
    }

    // 第二次中序遍历,修改节点的值
    void changeVal(TreeNode *cur)
    {
        if (cur == NULL)
        {
            return;
        }

        // 左
        if (cur->left)
        {
            changeVal(cur->left);
        }

        // 中
        if (preNew == NULL)
        {
            // 当前驱节点指向空时,找到左子树的最左叶子节点
            preOld->val = cur->val; // 记录旧值
            cur->val = sum;         // 更新值
            preNew = cur;           // 更新前驱节点指针
        }
        else
        {
            tmp->val = cur->val;                  // 记录当前节点的旧值
            cur->val = preNew->val - preOld->val; // 更新当前节点的值
            preOld->val = tmp->val;               // 更新前驱节点旧值
            preNew = cur;                         // 更新前驱节点
        }

        // 右
        if (cur->right)
        {
            changeVal(cur->right);
        }
    }
};
复制代码

法2

对二叉搜索树进行中序遍历可以得到一个有序数组,数组元素更新的规律可以看做是从后向前累加,所以可以使用逆向中序遍历,即遍历顺序为 右->中->左

  • 使用递归方法
    • 1、确定递归的参数与返回值
      • 参数:当前节点 cur 以及 前驱节点 pre
    • 2、确定递归的终止条件
      • 遇到空节点直接返回
    • 3、确定单层循环的逻辑
      • 先遍历右孩子,
      • 遍历根节点,同时修改节点元素的值
      • 最后左孩子

代码

class Solution
{
public:
    TreeNode *convertBST(TreeNode *root)
    {
        traversal(root);
        return root;
    }

private:
    TreeNode *pre = new TreeNode(0); // 前驱节点值初始为0
    void traversal(TreeNode *cur)
    {
        if (cur == NULL)
        {
            return;
        }

        // 右
        if (cur->right)
        {
            traversal(cur->right);
        }

        
        // 当前节点的值等于前一个节点的值+当前节点的值
        cur->val = pre->val + cur->val;
        pre = cur;
        // 左
        if (cur->left)
        {
            traversal(cur->left);
        }
    }
};
复制代码

猜你喜欢

转载自juejin.im/post/7033568084543668238
今日推荐