leetcode----538.把二叉搜索树转换为累加树(逆中序遍历和Morrirs中序遍历+Morrirs前中序遍历模板)

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

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

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

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

示例:

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

输入:root = [0,null,1]
输出:[1,null,1]

输入:root = [1,0,2]
输出:[3,3,2]

输入:root = [3,2,4,1]
输出:[7,9,4,10]

思路:

  • 中序遍历

二叉搜索树,中序遍历为升序序列,那么逆中序遍历,即按右子树、根节点、左子树遍历,则是降序序列。我们要使得每个节点 node 的新值等于原树中大于或等于 node.val 的值之和(前面说了,中序遍历为升序)。也就是说,该新树中,该节点的值等于自己的值+中序遍历序列该位置右边的所有节点值,有些类似于前缀和,我们将这个升序序列变成降序序列,然后再求各个节点对应的前缀和就行了。

class Solution {
    private int sum;
    public TreeNode convertBST(TreeNode root) {
        if(root == null) return null;
        sum = 0;
        med(root);
        return root;
    }

    private void med(TreeNode node){
        if(node == null) {
            return;
        }

        med(node.right);
        sum += node.val;
        node.val = sum;
        med(node.left);
    }
}

//不使用全局变量的反中序遍历
class Solution {
    public TreeNode convertBST(TreeNode root) {
        med(root, 0);
        return root;
    }

    private int med(TreeNode node, int rightSum){
        if(node == null) {
            return rightSum;
        }

        node.val += med(node.right, rightSum);
        return med(node.left, node.val);
    }
}
  • Morrirs中序遍历

模板如下:

递归和使用栈的树的遍历实现的算法的空间复杂度都是O(h).

实现:

记当前节点为cur

  1. 如果cur无左孩子,cur向右移动(cur = cur.right)
  2. 如果cur有左孩子,找到cur左子树上最右叶子节点,记为mostRight
    1. 如果mostRight的右孩子为空,让其指向cur节点,cur向左移动(cur = cur.left)
    2. 如果mostRight的右孩子指向cur(当前节点),让其指向空,cur向右移动(cur = cur.right)

实质:建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次

有左子树的节点,第一次到达是为了寻找前驱节点,使其右孩子指向当前节点,第二次到达因为遍历到了当前节点左子树的最右叶子节点,需要遍历当前节点的右子树,需要将指针重新指向当前节点。

public static void morrisPre(Node head) {
    if(head == null){
        return;
    }
    Node cur = head;
    Node mostRight = null;
    while (cur != null){
        // cur表示当前节点,mostRight表示cur的左孩子的最右节点
        mostRight = cur.left;
        if(mostRight != null){
            // cur有左孩子,找到cur左子树最右节点
            while (mostRight.right !=null && mostRight.right != cur){
                mostRight = mostRight.right;
            }
            // mostRight的右孩子指向空,让其指向cur,cur向左移动
            if(mostRight.right == null){
                mostRight.right = cur;
                //当前节点左子树的最右节点的右子树为空
                //说明当前节点未被遍历,此时为第一次遇到该节点,
                //为前序遍历的位置之一
                //前序遍历
                cur = cur.left;
                continue;
            }else {
                // mostRight的右孩子指向cur,让其指向空,cur向右移动
                mostRight.right = null;
                //当前节点左子树最右节点的右孩子指向当前节点
                //说明是第二次遇到,当前节点的左子树已经遍历完了
                //为中序遍历的位置之一
                //中序遍历
            }
        }else {
       		//此时对应当前节点的左子树为空
       		//也是第一次遇到该节点,为前序和中序遍历的位置之一
            //前序遍历
            //中序遍历
        }
        //中序遍历也可以写在这里,只需要在这遍历一次
        cur = cur.right;
    }
}

这个是正序的。需要稍微改造一下

/**
 * 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) {
        if(root == null){
            return null;
        }

        TreeNode cur = root;
        TreeNode mostleft = null;
        int rightSum = 0;
        while(cur != null){
            // System.out.println(cur.val);
            mostleft = cur.right;
            
            if(mostleft != null){
                //寻找当前节点右子树的最左节点
                while(mostleft.left != null && mostleft.left != cur){
                    mostleft = mostleft.left;
                }

                //找到最左节点,将最左节点的    左子树  作为当前节点的前驱节点,继续向右
                //注意,此时 mostleft 指向的是当前节点右子树中的最左节点,需要判断的是该最左节点的   左孩子是否为空
                //第一次写的时候,这块写成了most == null,找了半天才找到问题
                if(mostleft.left == null){
                    mostleft.left = cur;
                    cur = cur.right;
                    continue;
                } else {
                    //当前节点的右子树已经遍历完了,将树恢复原状
                    mostleft.left = null;
                }
            }

            //走到这有两种情况:1.当前节点的右子树为空, 2.当前节点的右子树已经遍历完了
            //中序遍历位置
            rightSum += cur.val;
            cur.val = rightSum;
            cur = cur.left;
        }
        return root;
    }
}

整理思路,记录博客,以便复习。若有误,望指正~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值