Files
leetcode-master/problems/0538.把二叉搜索树转换为累加树.md
2021-12-28 23:29:06 +08:00

317 lines
8.5 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210924105952.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 538.把二叉搜索树转换为累加树
[力扣题目链接](https://leetcode-cn.com/problems/convert-bst-to-greater-tree/)
给出二叉 搜索 树的根节点该树的节点值各不相同请你将其转换为累加树Greater Sum Tree使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
示例 1
![538.把二叉搜索树转换为累加树](https://img-blog.csdnimg.cn/20201023160751832.png)
* 输入:[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 之间。
* 树中的所有值 互不相同 。
* 给定的树为二叉搜索树。
# 思路
一看到累加树,相信很多小伙伴都会疑惑:如何累加?遇到一个节点,然后在遍历其他节点累加?怎么一想这么麻烦呢。
然后再发现这是一棵二叉搜索树,二叉搜索树啊,这是有序的啊。
那么有序的元素如果求累加呢?
**其实这就是一棵树,大家可能看起来有点别扭,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13],是不是感觉这就简单了。**
为什么变成数组就是感觉简单了呢?
因为数组大家都知道怎么遍历啊,从后向前,挨个累加就完事了,这换成了二叉搜索树,看起来就别扭了一些是不是。
那么知道如何遍历这个二叉树,也就迎刃而解了,**从树中可以看出累加的顺序是右中左,所以我们需要反中序遍历这个二叉树,然后顺序累加就可以了**。
## 递归
遍历顺序如图所示:
![538.把二叉搜索树转换为累加树](https://img-blog.csdnimg.cn/20210204153440666.png)
本题依然需要一个pre指针记录当前遍历节点cur的前一个节点这样才方便做累加。
pre指针的使用技巧我们在[二叉树:搜索树的最小绝对差](https://programmercarl.com/0530.二叉搜索树的最小绝对差.html)和[二叉树:我的众数是多少?](https://programmercarl.com/0501.二叉搜索树中的众数.html)都提到了,这是常用的操作手段。
* 递归函数参数以及返回值
这里很明确了,不需要递归函数的返回值做什么操作了,要遍历整棵树。
同时需要定义一个全局变量pre用来保存cur节点的前一个节点的数值定义为int型就可以了。
代码如下:
```
int pre; // 记录前一个节点的数值
void traversal(TreeNode* cur)
```
* 确定终止条件
遇空就终止。
```
if (cur == NULL) return;
```
* 确定单层递归的逻辑
注意**要右中左来遍历二叉树** 中节点的处理逻辑就是让cur的数值加上前一个节点的数值。
代码如下:
```
traversal(cur->right); // 右
cur->val += pre; // 中
pre = cur->val;
traversal(cur->left); // 左
```
递归法整体代码如下:
```CPP
class Solution {
private:
int pre; // 记录前一个节点的数值
void traversal(TreeNode* cur) { // 右中左遍历
if (cur == NULL) return;
traversal(cur->right);
cur->val += pre;
pre = cur->val;
traversal(cur->left);
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
```
## 迭代法
迭代法其实就是中序模板题了,在[二叉树:前中后序迭代法](https://programmercarl.com/二叉树的迭代遍历.html)和[二叉树:前中后序统一方式迭代法](https://programmercarl.com/二叉树的统一迭代法.html)可以选一种自己习惯的写法。
这里我给出其中的一种,代码如下:
```CPP
class Solution {
private:
int pre; // 记录前一个节点的数值
void traversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->right; // 右
} else {
cur = st.top(); // 中
st.pop();
cur->val += pre;
pre = cur->val;
cur = cur->left; // 左
}
}
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
```
# 总结
经历了前面各种二叉树增删改查的洗礼之后,这道题目应该比较简单了。
**好了,二叉树已经接近尾声了,接下来就是要对二叉树来一个大总结了**
# 其他语言版本
## Java
```Java
class Solution {
int sum;
public TreeNode convertBST(TreeNode root) {
sum = 0;
convertBST1(root);
return root;
}
// 按右中左顺序遍历,累加即可
public void convertBST1(TreeNode root) {
if (root == null) {
return;
}
convertBST1(root.right);
sum += root.val;
root.val = sum;
convertBST1(root.left);
}
}
```
## Python
**递归**
```python3
# 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 __init__(self):
self.pre = TreeNode()
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
'''
倒序累加替换:
[2, 5, 13] -> [[2]+[1]+[0], [2]+[1], [2]] -> [20, 18, 13]
'''
self.traversal(root)
return root
def traversal(self, root: TreeNode) -> None:
# 因为要遍历整棵树,所以递归函数不需要返回值
# Base Case
if not root:
return None
# 单层递归逻辑:中序遍历的反译 - 右中左
self.traversal(root.right) # 右
# 中节点用当前root的值加上pre的值
root.val += self.pre.val # 中
self.pre = root
self.traversal(root.left) # 左
```
## Go
弄一个sum暂存其和值
```go
//右中左
func bstToGst(root *TreeNode) *TreeNode {
var sum int
RightMLeft(root,&sum)
return root
}
func RightMLeft(root *TreeNode,sum *int) *TreeNode {
if root==nil{return nil}//终止条件,遇到空节点就返回
RightMLeft(root.Right,sum)//先遍历右边
temp:=*sum//暂存总和值
*sum+=root.Val//将总和值变更
root.Val+=temp//更新节点值
RightMLeft(root.Left,sum)//遍历左节点
return root
}
```
## JavaScript
递归
```javascript
var convertBST = function(root) {
let pre = 0;
const ReverseInOrder = (cur) => {
if(cur) {
ReverseInOrder(cur.right);
cur.val += pre;
pre = cur.val;
ReverseInOrder(cur.left);
}
}
ReverseInOrder(root);
return root;
};
```
迭代
```javascript
var convertBST = function (root) {
let pre = 0;
let cur = root;
let stack = [];
while (cur !== null || stack.length !== 0) {
while (cur !== null) {
stack.push(cur);
cur = cur.right;
}
cur = stack.pop();
cur.val += pre;
pre = cur.val;
cur = cur.left;
}
return root;
};
```
##C
递归
```c
int pre;
void traversal(struct TreeNode* node) {
if(!node)
return ;
traversal(node->right);
node->val = node->val + pre;
pre = node->val;
traversal(node->left);
}
struct TreeNode* convertBST(struct TreeNode* root){
pre = 0;
traversal(root);
return root;
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>