二叉搜索树在线OJ题讲解

简介: 二叉搜索树在线OJ题讲解
二叉树创建字符串

我们首先进行题目的解读:

大概意思就是用()把每个节点的值给括起来,然后再经过一系列的省略的来得到最后的结果

大家仔细观察题目给出的列子就可以发现,其实这个题目可以大致分为三种情况:

1 节点的左孩子和右孩子都在,不能省略括号

2 节点没有左孩子只有右孩子,不能省略括号

3 节点没有右孩子只有左孩子,可以省略这个空节点的括号,用来区分第二种情况,保证了一个字符串只能对应一个二叉搜索树

就比如例1:

例子中给出的初步转化的字符串是没有经过省略的,其实4后面还要加上两个空括号才完美,这里题目有一点小误差

就比如节点2他的右孩子节点是空的,所以那个括号就要省略,4和3的两个括号都省略,就可以得到最后的结果了

代码如下:

我们首先定义一个字符串,如果根节点root为空,就直接返回这个空字符串,不为空先+=root内存储的val

如果root的左孩子节点或者root的右孩子节点不为空的话,就要把root的左孩子节点的val用括号括起来,就算左孩子为空这个括号也不能省略

如果root的右孩子不为空就要把root的右孩子用括号括起来,最后 返回时str即可

class Solution {
public:
    string tree2str(TreeNode* root) 
    {
        string str;
        if(root==nullptr)
            return str;
        str+=to_string(root->val);
        if(root->left||root->right)
       { 
        str+='(';
        str+=tree2str(root->left);
        str+=')';
        }
        if(root->right)
        {
         str+='(';
         str+=tree2str(root->right);
         str+=')';
        }
        return str;
    }
};
二叉树的分层遍历

这个题目是一个典型的vector里面套vector(int)的题目,他最后返回的是双层结构

我们可以用到一个队列,这个队列用来存放节点,而双层的vector容器就用来存放层序遍历节点的val,最后返回

我们先将root节点放进队列,然后当他出来后,将他的左右孩子节点放入队列,只要队列不为空不就继续

完整代码如下:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        queue<TreeNode*> q;
        vector<vector<int>> vv;
        if(root==nullptr)
            return vv;
        int levelsize=0;
        q.push(root);
        levelsize=q.size();
        while(!q.empty())
        {
            vector<int> v;
            while(levelsize--)
        {
            TreeNode* node=q.front();
            q.pop();
            v.push_back(node->val);
            if(node->left)
                q.push(node->left);
            if(node->right)
                q.push(node->right);
        }
        vv.push_back(v);
        levelsize=q.size();
        }
        return vv;
    }
};

自底向上的层序遍历,我们就偷懒直接用上面的代码,用一个reverse函数反转即可:

将vv的迭代器反转

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> q;
        vector<vector<int>> vv;
        if(root==nullptr)
            return vv;
        int levelsize=0;
        q.push(root);
        levelsize=q.size();
        while(!q.empty())
        {
            vector<int> v;
            while(levelsize--)
        {
            TreeNode* node=q.front();
            q.pop();
            v.push_back(node->val);
            if(node->left)
                q.push(node->left);
            if(node->right)
                q.push(node->right);
        }
        vv.push_back(v);
        levelsize=q.size();
        }
        reverse(vv.begin(),vv.end());
        return vv;
    }
};
最近公共祖先

其实我们画图之后可以看到,公共祖先节点有几个很明显的特征:

如果一个节点在本节点的左子树,另一个在本节点的右子树上,那么这个节点就是那两个节点的公共祖先

如果两个节点都在一个节点的左或者右我们就用一个辅助函数来递归完成,都在左边或者右边那么两个节点之中有一个一定时他们两个节点的公共祖先

完整代码如下:

class Solution {
public:
//判断是否在这个树上
bool isintree(TreeNode* root,TreeNode* x)
{
    if(root==nullptr)
        return false;
    if(root==x)
        return true;
    return isintree(root->left,x)||isintree(root->right,x);
}
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root==p||root==q)
            return root;
        bool pinleft=isintree(root->left,p);
        bool pinright=isintree(root->right,p);
        bool qinleft=isintree(root->left,q);
        bool qinright=isintree(root->right,q);
        //一个在左一个在右
        if((pinleft&&qinright)||(pinright&&qinleft))
            return root;
        //都在左
        if(pinleft&&qinleft)
            return lowestCommonAncestor(root->left,p,q);
        //都在右
        if(pinright&&qinright)
            return lowestCommonAncestor(root->right,p,q);
        return nullptr;
    }
};
二叉树搜索树转换成排序双向链表

我们观察可以看到,最后排序出来的双向链表是一个有序的链表,而二叉搜索树的中序遍历一定是一个有序的序列,所以我们在这里要定义一个中序遍历的函数来用到解题

我们需要用到一个辅助的prev节点,令cur的left为prev,如果prev不为空就令prev的right为cur即可

class Solution {
public:
  void inorder(TreeNode* cur,TreeNode*& prev)
  {
    if(cur==nullptr)
      return;
    inorder(cur->left,prev);
    cur->left=prev;
    if(prev)
      prev->right=cur;
    prev=cur;
    inorder(cur->right,prev);
  }
    TreeNode* Convert(TreeNode* pRootOfTree) 
  {
    TreeNode* prev=nullptr;
    inorder(pRootOfTree,prev);
    TreeNode* head=pRootOfTree;
    while(head&&head->left)
    {
      head=head->left;
    }
    return head;
    }
};
前序中序还原二叉树

本题是根据前序和中序来构造二叉树,我们知道前序的第一个节点就是二叉树的根节点,而中序中根节点左边的就是左子树,右边为右子树,然后再遍历前序第二个节点,再次带入中序,前序的“根节点”能够将中序分为两个区间,分为区间后我们再递归两个区间

完整代码如下:

class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder,vector<int>& inorder,int& prei,int begin,int end)
    {
        if(begin>end)
            return nullptr;
        TreeNode* root=new TreeNode(preorder[prei]);
        int rooti=begin;
        while(rooti<=end)
        {
            if(inorder[rooti]==preorder[prei])
                break;
            else
                rooti++;
        }
        prei++;
        root->left=_buildTree(preorder,inorder,prei,begin,rooti-1);
        root->right=_buildTree(preorder,inorder,prei,rooti+1,end);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        int i=0;
        return _buildTree(preorder,inorder,i,0,inorder.size()-1);
    }
};
中序和后序还原二叉树

中序和后续构造二叉树就和前序和后续构造一样,转变一个思路:

但是要注意,由于后序是左右根,所以要先递归右在递归左

class Solution {
public:
    TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder,int& prei,int begin,int end)
    {
        if(begin>end)
            return nullptr;
        TreeNode* root=new TreeNode(postorder[prei]);
        int rooti=begin;
        while(rooti<=end)
        {
            if(inorder[rooti]==postorder[prei])
                break;
            else
                rooti++;
        }
        prei--;
        
        root->right=_buildTree(inorder,postorder,prei,rooti+1,end);
        root->left=_buildTree(inorder,postorder,prei,begin,rooti-1);
        
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int i= postorder.size()-1;
        return _buildTree(inorder,postorder,i,0,inorder.size()-1);
    }
};
相关文章
|
Java Python Windows
软件安装(四):Pycharm安装详细教程
本文提供了一个详细的PyCharm安装教程,包括下载、安装和使用步骤,特别强调了在Windows环境下安装时不要选择关联.py文件的选项,并说明了如何配置系统环境变量Path以及激活账号密码。
371 1
软件安装(四):Pycharm安装详细教程
|
10月前
|
开发工具 开发者 git
IntelliJ IDEA 插件推荐:提升开发效率的神器
本文介绍了 IntelliJ IDEA 的多个实用插件,涵盖从提高开发效率到美化界面的各个方面。
1239 1
|
存储 Kubernetes 调度
Kubernetes Pod深度解析:构建可靠微服务的秘密武器(上)
本文旨在全面而深入地探讨Kubernetes(K8s)中的Pod概念,为读者提供对其核心特性和应用场景的深入理解。Pod作为Kubernetes的最小部署单元,承载着容器化应用的核心功能,是构建云原生应用的重要基石。
STM32学习笔记(4) 高级定时器-两路互补的PWM输出(带死区和刹车控制)
原理:当捕捉到信号的跳变沿时,将CNT的值所存到捕获寄存器CCR中,然后把两次的值相减,就可以得到脉宽或者频率。
2973 0
|
存储 程序员
8086/8088微处理器【微机原理】1
8086/8088微处理器【微机原理】1
400 0
|
机器学习/深度学习 自然语言处理 搜索推荐
ModelScope 魔塔社区初探
第一次接触 ModelScope,记录下在 macOS 上的安装过程。
7590 9
|
BI 网络安全 数据安全/隐私保护
无线电HAM:业余无线电入门【无线电操作人员考证】【干货收藏】【网络安全进阶】
无线电HAM:业余无线电入门【无线电操作人员考证】【干货收藏】【网络安全进阶】
1781 0
无线电HAM:业余无线电入门【无线电操作人员考证】【干货收藏】【网络安全进阶】
|
存储 安全 小程序
如何使用 FlowUs 、Notion 等笔记软件中进行文件管理?
文件管理中常见的问题 缺少秩序 相信很多人从学生时代,便开始被文件管理所苦恼。这一状态甚至延伸到了进入职场以后。在我们的笔记本桌面文件夹或者下载文件夹,经常可以看到各种堆积如山的文件十分混乱摆放在一起。我们看到的是一座文件堆积的垃圾山。在我们需要的时候,却经常找不到自己所需要的文件。我们的文件缺少秩序,是文件管理所遇到的第一个问题。究其原因,是因为我们懒,没有及时整理文件吗?或许是,但这不是全部原因,也不是主要原因。
797 0
如何使用 FlowUs 、Notion 等笔记软件中进行文件管理?
|
前端开发 Java 应用服务中间件
SpringBoot的Web开发支持【超详细【一篇搞定】果断收藏系列】上
SpringBoot的Web开发支持【超详细【一篇搞定】果断收藏系列】
SpringBoot的Web开发支持【超详细【一篇搞定】果断收藏系列】上
|
程序员 数据安全/隐私保护 C++
程序员数学基础【四、取模应用-判断奇偶数、判断素数、求两个数的最大公约数、水仙花数】(Python版本)(一)
程序员数学基础【四、取模应用-判断奇偶数、判断素数、求两个数的最大公约数、水仙花数】(Python版本)(一)
261 0
程序员数学基础【四、取模应用-判断奇偶数、判断素数、求两个数的最大公约数、水仙花数】(Python版本)(一)