【算法学习】剑指 Offer II 054. 所有大于等于节点的值之和|538|1038(java / c / c++ / python / go / rust)

简介: 给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。提醒一下,二叉搜索树满足下列约束条件: 节点的左子树仅包含键 小于 节点键的节点。 节点的右子树仅包含键 大于 节点键的节点。 左右子树也必须是二叉搜索树。

剑指 Offer II 054. 所有大于等于节点的值之和|538. 把二叉搜索树转换为累加树|1038. 把二叉搜索树转换为累加树:

给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。

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

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

样例 1

在这里插入图片描述

输入:
    root = [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]

提示

  • 树中的节点数介于 0104 之间。
  • 每个节点的值介于 -104104 之间。
  • 树中的所有值 互不相同
  • 给定的树为二叉搜索树。

分析

  • 这道算法题需要翻译一下,通俗易懂一点。
  • 二叉树,大家应该比较熟悉,一般就是正序遍历,中序遍历,后序遍历,然后遍历的过程中进行一些简单的逻辑。
  • 二叉搜索树是一种特殊的二叉树,题目已经做了解释。
  • 由于要把每个节点的值替换成树中大于或者等于该节点值的所有节点值之和,根据二叉搜索树的特点,其实就是将每个节点的值替换成自己的值累加右子树的值之和。
  • 首先这一样需要遍历每个节点,但是按照什么顺序是关键,你会发现常规正序遍历,中序遍历,后序遍历都不顺畅。
  • 按照题意来看,显然最好的顺序就是先遍历右子树,然后根节点,接着左子树这样是最顺畅的,仅需要一个变量不断累加节点值,遍历到哪个节点就累加哪个节点的值,然后赋值给这个节点,这正好和中序遍历是反正的,也就是逆中序遍历。
  • 遍历这种套娃结构,一般就是递归或者循环(利用栈数据结构),递归相对比较简单,但是受调用栈限制,循环其实就是用栈这样的数据结构模拟了调用栈,本质上差不多,但是调用次数可以更多,因为调用栈的空间一般比较有限,堆空间比起来要大的多,但是实现同样的功能,逻辑上也复杂一些。
  • 无论用递归还是用栈数据结构去做循环,都需要与树结构对应的额外内存空间,有一种非常巧妙的遍历方法叫做 Morris遍历 ,可以将非递归遍历中的空间复杂度降为O (1),具体的实现二当家的已经加了详细注释,大家也可以去百科一下,做深入了解。

题解

java

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode convertBST(TreeNode root) {
        int      sum = 0;
        TreeNode cur = root;

        while (cur != null) {
            if (cur.right == null) {
                // 没有比自己值大的孩子
                // 轮到处理自己
                sum += cur.val;
                cur.val = sum;
                // 自己也遍历完了,所以该遍历比自己小的
                cur = cur.left;
            } else {
                // 右孩子的最左边,比自己大的最小的孩子
                // 也就是自己前边最后一个遍历的节点,下一个就该是自己
                TreeNode leftmostOfRight = cur.right;
                while (leftmostOfRight.left != null
                    && leftmostOfRight.left != cur) {
                    leftmostOfRight = leftmostOfRight.left;
                }
                if (leftmostOfRight.left == null) {
                    // 没有做过关联,说明第一次到这里,关联当前节点,保证遍历完比自己大的最小节点之后可以轮到自己
                    leftmostOfRight.left = cur;
                    cur = cur.right;
                } else {
                    // 第二次遍历到这里,说明比自己大的都遍历完了
                    // 解除关系,还原树结构
                    leftmostOfRight.left = null;
                    // 轮到处理自己
                    sum += cur.val;
                    cur.val = sum;
                    // 自己也遍历完了,所以该遍历比自己小的
                    cur = cur.left;
                }
            }
        }

        return root;
    }
}

c

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */


struct TreeNode* convertBST(struct TreeNode* root){
    int sum = 0;
    struct TreeNode *cur = root;

    while (cur != NULL) {
        if (cur->right == NULL) {
            // 没有比自己值大的孩子
            // 轮到处理自己
            sum += cur->val;
            cur->val = sum;
            // 自己也遍历完了,所以该遍历比自己小的
            cur = cur->left;
        } else {
            // 右孩子的最左边,比自己大的最小的孩子
            // 也就是自己前边最后一个遍历的节点,下一个就该是自己
            struct TreeNode *leftmostOfRight = cur->right;
            while (leftmostOfRight->left != NULL
                   && leftmostOfRight->left != cur) {
                leftmostOfRight = leftmostOfRight->left;
            }
            if (leftmostOfRight->left == NULL) {
                // 没有做过关联,说明第一次到这里,关联当前节点,保证遍历完比自己大的最小节点之后可以轮到自己
                leftmostOfRight->left = cur;
                cur = cur->right;
            } else {
                // 第二次遍历到这里,说明比自己大的都遍历完了
                // 解除关系,还原树结构
                leftmostOfRight->left = NULL;
                // 轮到处理自己
                sum += cur->val;
                cur->val = sum;
                // 自己也遍历完了,所以该遍历比自己小的
                cur = cur->left;
            }
        }
    }

    return root;
}

c++

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        int sum = 0;
        TreeNode *cur = root;

        while (cur != nullptr) {
            if (cur->right == nullptr) {
                // 没有比自己值大的孩子
                // 轮到处理自己
                sum += cur->val;
                cur->val = sum;
                // 自己也遍历完了,所以该遍历比自己小的
                cur = cur->left;
            } else {
                // 右孩子的最左边,比自己大的最小的孩子
                // 也就是自己前边最后一个遍历的节点,下一个就该是自己
                TreeNode *leftmostOfRight = cur->right;
                while (leftmostOfRight->left != nullptr
                       && leftmostOfRight->left != cur) {
                    leftmostOfRight = leftmostOfRight->left;
                }
                if (leftmostOfRight->left == nullptr) {
                    // 没有做过关联,说明第一次到这里,关联当前节点,保证遍历完比自己大的最小节点之后可以轮到自己
                    leftmostOfRight->left = cur;
                    cur = cur->right;
                } else {
                    // 第二次遍历到这里,说明比自己大的都遍历完了
                    // 解除关系,还原树结构
                    leftmostOfRight->left = nullptr;
                    // 轮到处理自己
                    sum += cur->val;
                    cur->val = sum;
                    // 自己也遍历完了,所以该遍历比自己小的
                    cur = cur->left;
                }
            }
        }

        return root;
    }
};

python

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def convertBST(self, root: TreeNode) -> TreeNode:
        total = 0
        cur = root

        while cur:
            if not cur.right:
                # 没有比自己值大的孩子
                # 轮到处理自己
                total += cur.val
                cur.val = total
                # 自己也遍历完了,所以该遍历比自己小的
                cur = cur.left
            else:
                # 右孩子的最左边,比自己大的最小的孩子
                # 也就是自己前边最后一个遍历的节点,下一个就该是自己
                leftmostOfRight = cur.right
                while leftmostOfRight.left and leftmostOfRight.left != cur:
                    leftmostOfRight = leftmostOfRight.left

                if not leftmostOfRight.left:
                    # 没有做过关联,说明第一次到这里,关联当前节点,保证遍历完比自己大的最小节点之后可以轮到自己
                    leftmostOfRight.left = cur
                    cur = cur.right
                else:
                    # 第二次遍历到这里,说明比自己大的都遍历完了
                    # 解除关系,还原树结构
                    leftmostOfRight.left = None
                    # 轮到处理自己
                    total += cur.val
                    cur.val = total
                    # 自己也遍历完了,所以该遍历比自己小的
                    cur = cur.left

        return root
        

go

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func convertBST(root *TreeNode) *TreeNode {
    sum := 0
    cur := root

    for cur != nil {
        if cur.Right == nil {
            // 没有比自己值大的孩子
            // 轮到处理自己
            sum += cur.Val
            cur.Val = sum
            // 自己也遍历完了,所以该遍历比自己小的
            cur = cur.Left
        } else {
            // 右孩子的最左边,比自己大的最小的孩子
            // 也就是自己前边最后一个遍历的节点,下一个就该是自己
            leftmostOfRight := cur.Right
            for leftmostOfRight.Left != nil && leftmostOfRight.Left != cur {
                leftmostOfRight = leftmostOfRight.Left
            }
            if leftmostOfRight.Left == nil {
                // 没有做过关联,说明第一次到这里,关联当前节点,保证遍历完比自己大的最小节点之后可以轮到自己
                leftmostOfRight.Left = cur
                cur = cur.Right
            } else {
                // 第二次遍历到这里,说明比自己大的都遍历完了
                // 解除关系,还原树结构
                leftmostOfRight.Left = nil
                // 轮到处理自己
                sum += cur.Val
                cur.Val = sum
                // 自己也遍历完了,所以该遍历比自己小的
                cur = cur.Left
            }
        }
    }

    return root
}

rust

// Definition for a binary tree node.
// #[derive(Debug, PartialEq, Eq)]
// pub struct TreeNode {
//   pub val: i32,
//   pub left: Option<Rc<RefCell<TreeNode>>>,
//   pub right: Option<Rc<RefCell<TreeNode>>>,
// }
//
// impl TreeNode {
//   #[inline]
//   pub fn new(val: i32) -> Self {
//     TreeNode {
//       val,
//       left: None,
//       right: None
//     }
//   }
// }
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {
    pub fn convert_bst(root: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
        let mut sum = 0;
        let mut cur = root.clone();

        while cur.is_some() {
            if cur.as_ref().unwrap().borrow().right.is_none() {
                // 没有比自己值大的孩子
                // 轮到处理自己
                sum += cur.as_ref().unwrap().borrow().val;
                cur.as_ref().unwrap().borrow_mut().val = sum;
                // 自己也遍历完了,所以该遍历比自己小的
                cur = cur.unwrap().borrow().left.clone();
            } else {
                // 右孩子的最左边,比自己大的最小的孩子
                // 也就是自己前边最后一个遍历的节点,下一个就该是自己
                let mut leftmostOfRight = cur.as_ref().unwrap().borrow().right.clone();
                while leftmostOfRight.as_ref().unwrap().borrow().left.is_some()
                    && leftmostOfRight.as_ref().unwrap().borrow().left != cur {
                    leftmostOfRight = leftmostOfRight.unwrap().borrow().left.clone();
                }
                if leftmostOfRight.as_ref().unwrap().borrow().left.is_none() {
                    // 没有做过关联,说明第一次到这里,关联当前节点,保证遍历完比自己大的最小节点之后可以轮到自己
                    leftmostOfRight.unwrap().borrow_mut().left = cur.clone();
                    cur = cur.unwrap().borrow().right.clone();
                } else {
                    // 第二次遍历到这里,说明比自己大的都遍历完了
                    // 解除关系,还原树结构
                    leftmostOfRight.unwrap().borrow_mut().left = Option::None;
                    // 轮到处理自己
                    sum += cur.as_ref().unwrap().borrow().val;
                    cur.as_ref().unwrap().borrow_mut().val = sum;
                    // 自己也遍历完了,所以该遍历比自己小的
                    cur = cur.unwrap().borrow().left.clone();
                }
            }
        }

        root
    }
}

在这里插入图片描述


原题传送门:https://leetcode-cn.com/problems/w6cpku/

原题传送门:https://leetcode-cn.com/problems/convert-bst-to-greater-tree/

原题传送门:https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/


非常感谢你阅读本文~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://developer.aliyun.com/profile/sqd6avc7qgj7y 博客原创~

相关文章
|
11天前
|
机器学习/深度学习 人工智能 算法
猫狗宠物识别系统Python+TensorFlow+人工智能+深度学习+卷积网络算法
宠物识别系统使用Python和TensorFlow搭建卷积神经网络,基于37种常见猫狗数据集训练高精度模型,并保存为h5格式。通过Django框架搭建Web平台,用户上传宠物图片即可识别其名称,提供便捷的宠物识别服务。
142 55
|
26天前
|
搜索推荐 Python
利用Python内置函数实现的冒泡排序算法
在上述代码中,`bubble_sort` 函数接受一个列表 `arr` 作为输入。通过两层循环,外层循环控制排序的轮数,内层循环用于比较相邻的元素并进行交换。如果前一个元素大于后一个元素,就将它们交换位置。
126 67
|
26天前
|
存储 搜索推荐 Python
用 Python 实现快速排序算法。
快速排序的平均时间复杂度为$O(nlogn)$,空间复杂度为$O(logn)$。它在大多数情况下表现良好,但在某些特殊情况下可能会退化为最坏情况,时间复杂度为$O(n^2)$。你可以根据实际需求对代码进行调整和修改,或者尝试使用其他优化策略来提高快速排序的性能
118 61
|
28天前
|
算法 数据安全/隐私保护 开发者
马特赛特旋转算法:Python的随机模块背后的力量
马特赛特旋转算法是Python `random`模块的核心,由松本真和西村拓士于1997年提出。它基于线性反馈移位寄存器,具有超长周期和高维均匀性,适用于模拟、密码学等领域。Python中通过设置种子值初始化状态数组,经状态更新和输出提取生成随机数,代码简单高效。
105 63
|
20天前
|
机器学习/深度学习 人工智能 算法
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
宠物识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了37种常见的猫狗宠物种类数据集【'阿比西尼亚猫(Abyssinian)', '孟加拉猫(Bengal)', '暹罗猫(Birman)', '孟买猫(Bombay)', '英国短毛猫(British Shorthair)', '埃及猫(Egyptian Mau)', '缅因猫(Maine Coon)', '波斯猫(Persian)', '布偶猫(Ragdoll)', '俄罗斯蓝猫(Russian Blue)', '暹罗猫(Siamese)', '斯芬克斯猫(Sphynx)', '美国斗牛犬
110 29
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
|
1天前
|
存储 算法 Python
文件管理系统中基于 Python 语言的二叉树查找算法探秘
在数字化时代,文件管理系统至关重要。本文探讨了二叉树查找算法在文件管理中的应用,并通过Python代码展示了其实现过程。二叉树是一种非线性数据结构,每个节点最多有两个子节点。通过文件名的字典序构建和查找二叉树,能高效地管理和检索文件。相较于顺序查找,二叉树查找每次比较可排除一半子树,极大提升了查找效率,尤其适用于海量文件管理。Python代码示例包括定义节点类、插入和查找函数,展示了如何快速定位目标文件。二叉树查找算法为文件管理系统的优化提供了有效途径。
33 5
|
1天前
|
存储 缓存 算法
探索企业文件管理软件:Python中的哈希表算法应用
企业文件管理软件依赖哈希表实现高效的数据管理和安全保障。哈希表通过键值映射,提供平均O(1)时间复杂度的快速访问,适用于海量文件处理。在Python中,字典类型基于哈希表实现,可用于管理文件元数据、缓存机制、版本控制及快速搜索等功能,极大提升工作效率和数据安全性。
19 0
|
1月前
|
机器学习/深度学习 算法 大数据
蓄水池抽样算法详解及Python实现
蓄水池抽样是一种适用于从未知大小或大数据集中高效随机抽样的算法,确保每个元素被选中的概率相同。本文介绍其基本概念、工作原理,并提供Python代码示例,演示如何实现该算法。
35 1
|
1月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
垃圾识别分类系统。本系统采用Python作为主要编程语言,通过收集了5种常见的垃圾数据集('塑料', '玻璃', '纸张', '纸板', '金属'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对图像数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。然后使用Django搭建Web网页端可视化操作界面,实现用户在网页端上传一张垃圾图片识别其名称。
84 0
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
|
1月前
|
机器学习/深度学习 人工智能 算法
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
手写数字识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Flask框架,开发网页端操作平台,实现用户上传一张图片识别其名称。
92 0
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型