题目链接: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;
}
};