[剑指offer]重建二叉树

第一层 二叉树遍历

题目介绍
在这里插入图片描述

给定一个(前序遍历)字符串,构建二叉树,并且以中序打印,#==nullpet , ’ ‘==空树

如果忘记请看这篇博客----->回顾二叉树概念


在这里插入图片描述

思路

  1. 前序是先根遍历,在左子树,右子树
  2. 分别对树的左右区间进行构造
  3. 根据# 进行构建树那么就会一下俩种情况

碰到一个#就说明当前的构建的区间已经全部构建完毕,已经没有元素可以构造了(类似中序遍历一直走到叶子节点)
而碰到##就代表遇到的是叶子节点代表左右区间都构建完成(后续遍历一直走到根节点)
在这里插入图片描述


代码:

#include<iostream>
using namespace std;
struct TreeNode 
{
      char val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(char x) : val(x), left(NULL), right(NULL) {}
  };

TreeNode*Construction(string &d1,int& i)
{
    if(d1[i]=='#')//半边子树已经构建完成
    {
        return nullptr;
    }
    TreeNode *root=new TreeNode(d1[i]);//构造节点
    
    root->left=Construction(d1,++i);//构造左子树
    root->right=Construction(d1,++i);//构造右子树
    
    return root;
}
  void Print(TreeNode * d1)//打印
  {
      if(d1==nullptr)
          return;
      Print(d1->left);
      cout<<d1->val<<' ';
       Print(d1->right);
  }
int main()
{
    string d1;
    cin>>d1;
    int i=0;
  Print(Construction(d1,i));
}

图解构建:

其实思路非常的简单但是他是如何构建的呢?老样子画图
蓝线代表 :递归
橘线代表:回到调用位置
绿线代表:回到调用位置(节点构建完毕)


后面的节点构建也是如此

](https://img-blog.csdnimg.cn/328b110bc94b443fa18d4afbef05f0cf.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiA5Liq5q2j55u055qE55S35a2p,size_20,color_FFFFFF,t_70,g_se,x_16)



第二层 给定前中序构建一个二叉树


题目描述:

简单回顾:

前序遍历是 先根 在左子树 右子树
中序遍历是 先左子树 在根 右子树

思路:

前序每次都先根遍历,那么就可以依靠前序来确定根节点的位置,而中序遍历是先左子树在根,那么我们就可以中序中找前序确定好的根来划分左右子树,思路其实就俩句话,前序找根,中序划分左右子树


代码:

第一步
进行特殊处理,构建子函数进行递归构建树(原来的函数结构不满足递归构树的要求)

 TreeNode* reConstructBinaryTreeHelp(vector<int>& pre,int pre_start,int pre_end,vector<int>& vin,int vin_start,int vin_end)
    {
    
    }
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin)
    {
        if(pre.size()<vin.size())//特殊情况给你的序列不属于同一树
        {
            return nullptr;
        }
        
    
       
     return reConstructBinaryTreeHelp(pre,0,pre.size()-1,vin,0,pre.size()-1);//构建树,传pre和vin的有效区间,也可以看出我们传的是闭区间,就是【0,pre.size()-1】使用下标都可以访问到,vin也一样

第二步实现递归主逻辑

 TreeNode* reConstructBinaryTreeHelp(vector<int>& pre,int pre_start,int pre_end,vector<int>& vin,int vin_start,int vin_end)
    {
    
    
      //递归结束出口
        if(pre_start>pre_end||vin_start>vin_end)//如果他们的起始位置都大于他们的结束位置也就说明没有节点了
        {
            return nullptr;
        }
       TreeNode*root=new TreeNode(pre[pre_start]);//构造节点,前序是根
    
    
      for(int i=vin_start;i<=vin_end;i++)//在中序中划分左右区间,那么起始位置肯定是中序的的start末尾就是end
      {
          if(pre[pre_start]==vin[i])//中序找到根划分左右子树,并开始构建
          {
              //构建左子树,把控区间
              root->left=reConstructBinaryTreeHelp(pre, , ,vin, , );
              root->right=reConstructBinaryTreeHelp(pre, , ,vin, , );
              break;//走到这一步说明了树已经构建完毕
          }
          
      }
      return 0;
    
    }

第三步确定区间

其实这一块难的不是思路,思路很好想,但是把控他的区间却是这一到题的精髓,所以我们把他一一拎出来详细分析,这里还需要结合第二部划分左右子树循环这一块的代码,但是我会画图标明


第一次划分
在这里插入图片描述

左子树的:

pre_start

在第一次划分可以看出,pre_start其实就只需要指向下一个位置就可以了,前序是确定根的

vin_start

他的区间依旧是vin_start如第一次划分中所示

vin_end

end的位置就是当前root的前一个位置(i-1),那么root所在的位置就是 i(第二部分确定root位置的循环),且代码实现是一个闭区间,如第一次划分所示

pre_end

那么现在我们来想一个问题我们这么知道前序中左子树的节点个数?


根据图中所示前序的节点区间是[1,7],那么如何控制呢?那我们现在在想一想有啥和左子树节点密切相关的东西…………,哦中序遍历的start 与 end 不就指向区间不就是左子树的节点个数吗?对的


如上面分析得出vin_end==i-1,那么最终可以得出区间就是 pre_start+1(前序的起始位置) +i-1 -vin_start----> 最终得到的区间就是 pre_start+i-vin_start

右子树的:

pre_start

由图可以看出,右子树对于前序来说其实就上左子树pre_end+1 也就是 pre_start+i-vin_start+1

pre_end

他的结束位置也很简单如图所示,依旧还是pre_end

vin_start

从图中可以看出被分割后右子树的区间起始位置就是 i(root节点)后面 也就是 i+1

vin_end

他的结束位置也很简单如图所示,依旧还是vin_end

代码:

     root->left= reConstructBinaryTreeHelp(pre,pre_start+1,  pre_start+i-vin_start       ,vin,vin_start ,i-1);
                root->right=reConstructBinaryTreeHelp(pre,  pre_start+i-vin_start+1           , pre_end,vin,i+1 , vin_end);


唠唠家常

多分析吧,一回生,二回熟,三回就是好基友,记得定期拿出来写一写
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值