代码随想录算法训练营第二十三天| 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 总结篇

669. 修剪二叉搜索树

题目链接

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

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

思路:根据题意可以有如下判断:
当我root的值比左边界小的时候,利用二叉搜索树的性质,可以向右继续遍历,继续做判断
当root的值比右边界大的时候,那么就向左遍历。

这两点不容易想,很容易忘。所以需要加强练习。

递归逻辑在注释里面:

class Solution {
public:
    // 1、确定递归函数的参数和返回值  因为是要遍历整棵树,做修改,
    // 其实不需要返回值也可以,我们也可以完成修剪(其实就是从二叉树中移除节点)的操作。
    // 但是有返回值,更方便,可以通过递归函数的返回值来移除节点。
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        // 2、确定函数的终止条件
        //  修剪的操作并不是在终止条件上进行的,所以就是遇到空节点返回就可以了。
        if (root == nullptr) return nullptr;

        // 3、确定单层递归逻辑
        // 3.1 如果root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点。
        if (root->val < low) {
            TreeNode* right = trimBST(root->right, low, high);
            return right;
        }

        // 3.2 如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。
        if (root->val > high) {
            TreeNode* left = trimBST(root->left, low, high);
            return left;
        }

        // 3.3 遍历整个树  如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。
        root->left = trimBST(root->left, low, high);
        root->right = trimBST(root->right, low, high);

        return root;
    }
};

总结: 本题的主要难点在于可以会判断左或者右满足条件,然后直接返回nullptr了,这样会导致删除的节点右子树或者左子树满足条件的情况被删除,缺少结果,所以要注意

本题还联动了前一天的删除二叉搜索树的节点那一题,那一题是删除单个节点,而这一题是删除一连串不满足条件的结果,但是思路很很像,需要再去理解。

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

题目链接

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

654.最大二叉树里面涉及到了给定一个数组构建一个二叉树,本题的思路和654这题一样,只需要保证每次的中间节点是数组中间的节点即可。

然后就是一个循环不变量的规则,左闭右开和左闭右闭一定要全程统一,否则在递归的过程会导致出现错误。

左闭右闭:

class Solution {
public:
    TreeNode* traversal(vector<int>& nums, int left, int right) {
        if (left > right) return nullptr;

        int mid = (right + left) / 2;

        TreeNode* node = new TreeNode(nums[mid]);
        node->left = traversal(nums, left, mid - 1);
        node->right = traversal(nums, mid + 1, right);

        return node;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return traversal(nums, 0, nums.size()-1);

    }
};

总结:本题在开始自己做的时候,在取中间节点的时候使用了 nums.size() / 2 这样会导致错误。应该用 (right + left) / 2; 在二分法中还要考虑左右加导致溢出,但是在数组中,这个一般不会导致溢出。

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

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

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

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

在这里插入图片描述

思路:本题的题目比较难读懂,每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
其实这就是一棵树,大家可能看起来有点别扭,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13],是不是感觉这就简单了。

转变了思路以后,就是一个右中左的遍历顺序:

class Solution {
public:
    int pre = 0;
    void traverasl(TreeNode* node) {
 
        if (node == nullptr) return;

        traverasl(node->right);
        node->val += pre;
        pre = node->val;
        traverasl(node->left);

    }

    TreeNode* convertBST(TreeNode* root) {
        pre = 0;
        traverasl(root);
        return root;
        
    }
};
class Solution {
public:
    void traversal(TreeNode* root) {
        stack<TreeNode*> st;

        TreeNode* cur = root;
        int pre = 0;
        while (cur != nullptr || !st.empty()) {
            if (cur != nullptr) {
                st.push(cur);
                cur = cur->right; // 右
            }
            else {
                cur = st.top();  // 中
                st.pop();
                cur->val += pre;
                pre = cur->val;
                cur = cur->left; // 左
            }
        }
    }

    TreeNode* convertBST(TreeNode* root) {
        int pre = 0;
        traversal(root);
        return root;
    }
};

总结:本题很简单,但是题目比较难读懂,但是如果用数组做了类比以后,就变得容易理解了,然后还是要多练才行。

二叉树总结篇

在每一道二叉树的题目中,我都使用了递归三部曲来分析题目,相信大家以后看到二叉树,看到递归,都会想:返回值、参数是什么?终止条件是什么?单层逻辑是什么?

按照代码随想录网站上的总结顺序进行复盘

二叉树收获比较多,因为之前没有学完全学过二叉树,跟着卡哥刷题,慢慢的就学会了很多,收获真的很大。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值