【代码训练营】day23 | 669. 修剪二叉搜索树 & 108.将有序数组转换为二叉搜索树 & 538.把二叉搜索树转换为累加树

所用代码 java

修剪二叉树 LeetCode 669

题目连接:修剪二叉树 LeetCode 669 - 中等

思路

本题是前面删除二叉树的结点那题的一个进阶版。删除边界的值的话肯定有一个最大边界,一个最小边界,删除的数可能是多个的。

递归法:

若结点值在边界之外,删除的情况有两种:

  1. 结点值小于low,那就去递归结点的右孩子(因为左孩子肯定小于low),右孩子中也有可能小于low的,所以应把该结点再次进行递归
  2. 结点值大于high,就递归左孩子(因为右孩子肯定大于high),但是左孩子也有可能大于high,就继续往左递归,返回正确构建好的二叉树
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        return traversal(root, low, high);
    }public TreeNode traversal(TreeNode root, int low, int high){
        if (root == null) return null;
        // 结点值小于low,就去递归右孩子
        if (root.val < low){
            return traversal(root.right, low, high);
        }
        // 结点值大于high,就去递归左孩子
        if (root.val > high){
            return traversal(root.left, low, high);
        }
        // 若root不在区间之类,返回的就是他的左右孩子
        // 因为最前面已经判断root的值和low与high大小关系了
        root.left = traversal(root.left, low, high);
        root.right = traversal(root.right, low, high);
        
        // 返回删除好的二叉搜索树
        return root;
    }
}

迭代法:

  1. 将root移至[low, high]区间 –左闭右闭
  2. 修剪左子树,左子树的值一定是小于high,就只用修剪小于low的值
  3. 修剪右子树,同理右子树的值一定大于low,所以只用考虑大于high的值
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) return null;
        // 1、将root移动到[low, high]区间
        while (root != null &&(root.val < low || root.val > high)){
            // 小于low往右走,大于low则往左走
            if (root.val < low) root = root.right;
            else root = root.left;
        }
        // 由于root在low和high之间,
        // 左边的值可能会小于low,但是左边的值一定小于high,不用考虑大于high的情况
        // 同理,右边的值一定是大于low的TreeNode cur = root; // 处理右边的指针
        // 2、修剪左区间 -- 左边就只用考虑小于low的情况
        while (cur != null){
            while (cur.left != null && cur.left.val < low){
                cur.left = cur.left.right;
            }
            cur = cur.left;
        }
​
        cur = root; // 处理右边的指针
        // 3、修剪右区间
        while (cur != null){
            while (cur.right != null && cur.right.val > high){
                cur.right = cur.right.left;
            }
            cur = cur.right;
        }return root;
    }
}

总结

本题看似简单,就是删除结点值,但是和上一题不同。因为本题需删除区间外的值,不是简单删除一个值,需递归的把不需要的结点给删除掉。

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

题目连接:将有序数组转换为二叉搜索树 LeetCode 108 - 简单

思路

这题属于构建二叉树的题目,由于数组是有序的,那么他的根结点就是数中间的那个值,然后我们以此递归构建二叉树。且该二叉树一定是平衡二叉树,因为左右结点的个数是相同的

递归:

  1. 找到根结点 – 数组的中间的值
  2. 以该值划分左右区间 ( 左右下标可以直接操作数组 )
  3. 递归构建二叉树
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return traversal(nums);
    }public TreeNode traversal(int[] nums){
        if (nums.length == 0) return null;
        // 以中间的值为根结点
        int rootValue = (nums[nums.length/2]);
        TreeNode root = new TreeNode(rootValue);
        // 只有一个结点 直接返回
        if (nums.length == 1) return root;// 切分左右子树区间 copyOfRange -- 左闭右开
        int[] left = Arrays.copyOfRange(nums, 0, nums.length/2);
        int[] right = Arrays.copyOfRange(nums, nums.length/2 + 1, nums.length);
​
​
        root.left = traversal(left);
        root.right = traversal(right);
        return root;
    }
}

由于数组可以直接用下标来操作,上边的代码可以进行优化,不用定义新的数组,节省空间:

直接用数组的下标来操作:左闭右开

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return traversal(nums, 0 , nums.length);
    }public TreeNode traversal(int[] nums, int begin, int end){
        if (begin == end) return null;
        // 以中间的值为根结点
        int mid = (begin + end) / 2;  // 这里是下标相加,不用担心爆内存
        int rootValue = (nums[mid]);
        TreeNode root = new TreeNode(rootValue);
        // 只有一个结点就之间返回
        if (end - begin == 1) return root;
​
        root.left = traversal(nums, begin, mid);
        root.right = traversal(nums, mid+1, end);
        return root;
    }
}

总结

只要清楚单调递增的数组,用中间的点为根结点,左边为左子树,右边为右子树,那我们就可以很轻松的构建出二叉搜索树,且该树一定是平衡二叉树。

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

题目链接:把二叉搜索树转换为累加树 LeetCode 538 - 中等

思路

无。没看懂题。。。。


该题的意思是,二叉树的某个结点的值为:比该结点大的所有值相加

一种容易看懂的暴力解法:

class Solution {
    List<Integer> list;
    public TreeNode convertBST(TreeNode root) {
        list = new ArrayList<>();
        sumOfTree(root, list);
        traversal(root);
        return root;
    }// 构建累加树
    public void traversal(TreeNode node){
        if (node == null) return;
        node.val = newVal(node.val);
        traversal(node.left);
        traversal(node.right);
    }// 二叉搜索树中序 => 从小到大递增
    public void sumOfTree(TreeNode node, List<Integer> list){
        if (node == null) return;
        sumOfTree(node.left, list);
        list.add(node.val);
        sumOfTree(node.right, list);
    }// 求比该结点大的值的和
    public int newVal(int val){
        int sum = 0;
        for (Integer i : list) {
            if (val <= i) {
                sum += i;
            }
        }
        return sum;
    }
}

其实双指针进行递归,可以一次递归就完成赋值加求和:

且二叉搜索树以中序(左 中 右 )为单调递增的树,所有我们可以反过来遍历(右 中 左),从大到小的进行累加:

class Solution {
    int pre;
    public TreeNode convertBST(TreeNode root) {
        pre = 0;
        traversal(root);
        return root;
    }public void traversal(TreeNode node){
        if (node == null) return;
        traversal(node.right); // 右
        node.val += pre; // 中
        // 再把该指针往上移
        pre = node.val;
        traversal(node.left); // 左
    }
}

另外,本题使用迭代法也非常简单,其迭代法就是中序遍历的模板,但是我们是以右 中 左的顺序进行的

迭代法:

class Solution {
    public TreeNode convertBST(TreeNode root) {
        Stack<TreeNode> stack = new Stack<TreeNode>();
        // pre用于记录前一个数值
        int pre = 0;
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()){
            if (cur != null){
                stack.push(cur);
                cur = cur.right; // 右
            }else {
                // 中
                cur = stack.pop();
                cur.val += pre;
                pre = cur.val;
                // 左
                cur = cur.left;
            }
        }
        return root;
    }
}

总结

本题就是一个双指针的题,关键在于如何进行遍历,使二叉搜索树单调递减排序,然后我们就可以从后往前累加他的值了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值