3月4日代码随想录修剪二叉搜索树、将有序数组转换为二叉树、把二叉搜索树转换为累加树

 669.修剪二叉搜索树

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

提示:

  • 树中节点数在范围 [1, 104] 内
  • 0 <= Node.val <= 104
  • 树中每个节点的值都是 唯一 的
  • 题目数据保证输入是一棵有效的二叉搜索树
  • 0 <= low <= high <= 104

思路

递归

第一想法是分为两个递归函数,一个判断左边是否小于low,一个判断右边是否大于high。另外还有一种情况,就是根节点的值在范围外,则直接递归左/右子树。

终止条件:节点值大于high或小于low

递归操作:当前节点左右子树进行递归

返回值:当前节点

 class Solution {
        public TreeNode trimBST(TreeNode root, int low, int high) {
            if(root==null){
                return null;
            }
            if(root.val>high){
                return trimBST(root.left,low,high);
            }
            if(root.val<low){
                return trimBST(root.right,low,high);
            }
            root.left=trimLow(root.left,low);
            root.right=trimHigh(root.right,high);
            
            return root;
        }
        public TreeNode trimLow(TreeNode node,int low){
            if(node==null){
                return null;
            }
            if (node.val >= low) {
                node.left=trimLow(node.left,low);
            }else {
                node=trimLow(node.right,low);
            }
            return node;
        }
        public TreeNode trimHigh(TreeNode node,int high){
            if(node==null){
                return null;
            }
            if (node.val <= high) {
                node.right=trimHigh(node.right,high);
            }else {
                node=trimHigh(node.left,high);
            }
            return node;
        }
    }

但实际上所有操作都可以在一个函数里实现,代码更加简洁。 

 class Solution {
        public TreeNode trimBST(TreeNode root, int low, int high) {
            if(root==null){
                return null;
            }
            if(root.val>high){
                return trimBST(root.left,low,high);
            }else if(root.val<low){
                return trimBST(root.right,low,high);
            }else{
                root.left=trimBST(root.left,low,high);
                root.right=trimBST(root.right,low,high);
                return root;
            }
            
         
        }
       
    }

迭代法

分别向左向右迭代,例如往左边迭代时,若当前节点左子节点值小于low,则将左子节点更新为左子节点的右节点。

class Solution {
        public TreeNode trimBST(TreeNode root, int low, int high) {
            while(root!=null&&(root.val<low||root.val>high)){
                if(root.val<low){
                    root=root.right;
                }else{
                    root=root.left;
                }
            }
            if (root == null) {
                return null;
            }
            for(TreeNode node=root;node.left!=null;){
                if (node.left.val < low) {
                    node.left=node.left.right;
                }else {
                    node=node.left;
                }
            }
            for(TreeNode node=root;node.right!=null;){
                if (node.right.val > high) {
                    node.right=node.right.left;
                }else {
                    node=node.right;
                }
            }
            return root;
        }

    }

108.将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 按 严格递增 顺序排列

思路

想要构造一棵二叉树,常见的方法是自顶向下构建,而要从一个严格有序数组构建一个二叉搜索树,我们首先需要找到根节点。若要使最后得出的二叉搜索树是平衡的,根结点应当选择数组中间的树,此时我们默认为中间靠左的节点,因为可能出现节点个数为偶数的情况。

整个递归思路可以视为找到数组某段中可以当作根节点的那个数。所以首先确定root,然后root的左节点为递归root左边部分找到的根节点,右节点为递归root右边部分找到的根节点。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return sort(nums,0,nums.length-1);
    }
    public TreeNode sort(int[] nums,int left,int right){
        if(left>right){
            return null;
        }
        int mid=(left+right)/2;
        TreeNode node=new TreeNode(nums[mid]);
        node.left=sort(nums,left,mid-1);
        node.right=sort(nums,mid+1,right);
        return node;
    }
}

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

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

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

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

提示:

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

思路

中序遍历/反序中序遍历法

使用两次中序遍历即可完成,第一次中序遍历将所有节点的值相加并存储,第二次中序遍历将总和赋给遍历节点,再将总和减去遍历节点原值,这样的时间复杂度就是O(n),同时因为题目的限制,所有结点的值加起来不会超int限制,所以很方便。

class Solution {
    int sum=0;
    public TreeNode convertBST(TreeNode root) {
        dfs1(root);
        dfs2(root);
        return root;
    }
    public void dfs1(TreeNode root){
        if(root==null){
            return;
        }
        dfs1(root.left);
        sum+=root.val;
        dfs1(root.right);
    }
    public void dfs2(TreeNode root){
        if(root==null){
            return;
        }
        dfs2(root.left);
        int tmp=root.val;
        root.val=sum;
        sum-=tmp;
        dfs2(root.right);
    }
}

但是其实使用了两次中序遍历就已经陷入思维定势了,此处我们可以反序进行中序遍历,记录过程中的节点之和,并更新当前节点值为节点之和,只需遍历一次二叉树。

class Solution {
    int sum = 0;

    public TreeNode convertBST(TreeNode root) {
        if (root != null) {
            convertBST(root.right);
            sum += root.val;
            root.val = sum;
            convertBST(root.left);
        }
        return root;
    }
}

Morris遍历

一样的morris,一样的看不懂

主要是利用二叉树中大量的空余空间。

    class Solution {
        public TreeNode convertBST(TreeNode root) {
            int sum = 0;
            TreeNode node = root;
            //借鉴线索二叉树的思想,充分利用二叉树中的空指针。考虑到遍历顺序为右中左,故当前节点的右子树遍历结束后应当遍历当前节点,故应将当前节点右子树中最后遍历的节点的左指针指向当前节点,
            // 即建立右孩子到父节点的联系,而其右子树最后遍历的节点一定为右子树中最左侧节点。
            while (node != null){
                if (node.right == null){//当前节点没有右孩子,优先遍历当前节点
                    sum += node.val;
                    node.val = sum;
                    node = node.left;//当前节点遍历结束,继续遍历其后继节点
                } else {
                    TreeNode succ = getSuccessor(node);//寻找当前节点的前驱节点
                    if (succ.left == null){//如果前驱节点左指针为空,将其指向当前节点,方便从当前节点的右子树寻找当前节点
                        succ.left = node;
                        node = node.right;//控制权优先转交给其右孩子
                    } else {//前驱节点左指针不为空,即已经建立线索
                        succ.left = null;//拆除当前节点的前驱节点的线索还原二叉树(该节点已经在上一次循环中被访问过,其左指针不再需要)
                        sum += node.val;//遍历当前节点
                        node.val = sum;
                        node = node.left;//继续遍历后继节点
                    }
                }
            }
            return root;
        }
        //建立一个函数寻找当前节点的前驱节点。
        public TreeNode getSuccessor(TreeNode node){
            TreeNode succ = node.right;
            //前驱节点一定在当前节点右子树的最左端,或前驱节点的左指针已经指向当前节点
            while (succ.left != null && succ.left != node){
                succ = succ.left;
            }
            return succ;
        }

    }

总结

二叉树的部分暂时结束了,递归法的使用越来越熟练,但是迭代法以及morris遍历还是似懂非懂,二刷时继续加强。

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值