当前位置: 首页 > article >正文

算法day20|669. 修剪二叉搜索树、将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

算法day20|669. 修剪二叉搜索树、将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

  • 669. 修剪二叉搜索树
  • 08.将有序数组转换为二叉搜索树
  • 538.把二叉搜索树转换为累加树

669. 修剪二叉搜索树

class Solution {
public:
    TreeNode* traversal(TreeNode* root, int low, int high)
    {
        if(root==nullptr)
        return nullptr;
        if(root->val<low)
        {
            TreeNode*right=traversal(root->right,low,high);
            return right;
        }
        if(root->val>high)
        {
            TreeNode*left=traversal(root->left,low,high);
            return left;
        }
        root->left=traversal(root->left,low,high);
        root->right=traversal(root->right,low,high);
        return root;
    }
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        return traversal(root,low,high);
    }
};

重点讲单层递归逻辑:首先,题目本质要求、返回值和逻辑三者是紧密结合的。题目要求返回最后树的根节点,那么我们的返回值肯定要选节点类型。再选择了返回值之后,我们移除节点的逻辑是什么样的呢?答:如果这个结点真的需要删除,那我们就用它可能成立的子树去替代,也就是说,这个结点被它的子树覆盖了。如果不是,该节点还是该节点。

本质就是检查是否“德不配位”:如果配位,你还在这个位置上;如果不配位,你就要下台,让你下面的子孙来顶替。当然,这子孙也要经过考验,所以实际是被考验后的子孙替代。

由于对当前结点的处理不需要借助孩子的信息,所以使用先序。当父亲确定好之后才能正常去遍历。

二叉树的修剪与删除的区别点在哪里?答:

  • 首先删除的核心操作在终止条件下进行,当遇到要删除的结点时,整个遍历过程其实已经结束了;而移除是对当前结点的处理。核心不同点终止条件里面是不能使用递归的,因为到这里递归一斤停止了;而对当前结点的处理是可以使用递归的。这也是判断是否区分两者的重要依据,所以使用和判断相辅相成。
  • 其次,

两者共同点:

  • 都是用root->left或者root->right来接收递归返回值,因为在每一次递归,结点都要经过审判,如果满足某种条件它就要被剔除或者替代,所以这是直接面向结点的、决定结点命运和生死的操作,所以直接用结点本身来接收。
  • 每一次返回的值是对当前结点的替代节点。可以是Null、可以是其他结点,也可以是它本身。这个和用root->left或者root->right来接收递归返回值是息息相关的,所以两者是统一考虑的。

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

class Solution {
public:
    TreeNode* traversal(vector<int>& nums,int left,int right)
    {
        if(left>right)
        return nullptr;
        int mid=(left+right)/2;
        TreeNode* root=new TreeNode(nums[mid]);
        root->left=traversal(nums,left,mid-1);
        root->right=traversal(nums,mid+1,right);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return traversal(nums,0,nums.size()-1);
    }
};

建立树肯定是从根节点往下建立的,所以关键是找到该插入数组中的哪一项。

我们在原数组中用两个指针,表示区间的边界(左闭右闭),通过计算,mid指针恰好指向要插入的元素。采用先序遍历,在当前结点处理完之后,左孩子用左区间,右孩子用右区间。

易错:

  • 终止条件,这里的终止条件不是数组的size为0,因为我们并没有对数组做任何的改变,我们只是用了两个指针来限定区间,所以终止条件肯定与两指针的位置有关。
  • 遵循循环不变量的原则,自始至终都遵循同一个区间标准,这里就是左闭右闭。

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

class Solution {
public:
    int pre=0;
    void traversal(TreeNode*root)
    {
        if(root==nullptr)
        return;
        traversal(root->right);
        root->val+=pre;
        pre=root->val;
        traversal(root->left);
        return;
    }
    TreeNode* convertBST(TreeNode* root) {
        if(root==nullptr)
        return nullptr;
        traversal(root);
        return root;
    }
};

做这题需要巧劲,我一开始是从前往后加的,时间复杂度高不说,中间过程还非常繁琐,导致出错。而正确的思路是从后往前加,用pre指针记录之前的值,从而实现了边遍历边加。所以有时候按常规思维做题很费劲的时候,不妨停下来,换一种思路再想一遍。


http://www.kler.cn/news/289135.html

相关文章:

  • 安嘉空间:智慧科技守护空间健康
  • C++基础多态
  • 爬虫练习(js逆向解密)
  • 中国水资源用水紧张程度数据(栅格/0.5度)
  • win10 +git配置+学习笔记
  • windows下安装docker操作步骤
  • 【C++第十六章】多态
  • 综合评价 | 基于层次-变异系数-正态云组合法的综合评价模型(Matlab)
  • Vulkan入门系列18 - 计算着色器(Compute Shader)
  • 阳台封窗是在保温上边还是把保温拆了之后封呢?
  • JsonCpp库的使用
  • SQL基础——MySQL的优化
  • SOHO建站
  • 【mysql】SQL语言的概述
  • java03
  • 深入探索Java中的分布式文件系统:从理论到实战
  • LeetCode_sql_day18(1841.联赛信息统计)
  • 维信小程序禁止截屏/录屏
  • React学习day03-components插件安装(仅基于火狐浏览器)、受控表单绑定、在React中获取dom、组件通信(组件间的数据传递)
  • 51单片机-串口通信关于SBUF的问题
  • elementui 表单 tab切换下个光标能不能改成enter键
  • 24数学建模国赛提供助攻(13——灰色系统理论)
  • 611.有效三角形的个数
  • 豆包MarsCode编程助手:让编程更简单
  • 七、场景加载
  • git中的分支是什么?分支有哪些好处?如何建立分支?
  • PyTorch Geometric(torch_geometric)简介
  • 行业首家!百度智能云通过中国信通院「H5 端人脸识别安全能力」测评
  • DORIS - DORIS注意事项(一)
  • C++:类的定义、实例化