天梯赛(GPLT/CCCC)二叉树

这篇博客探讨了如何通过后序和中序遍历重建二叉搜索树,并实现层序遍历。同时,文章介绍了如何处理二叉树的镜像翻转,以及检查是否为完全二叉搜索树,并给出层序遍历。此外,还涉及完全二叉树的层序遍历构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PTA L2-006 树的遍历

已知:节点个数 序遍历 序遍历

所求:层序遍历

解法:

中序遍历分左右 后序遍历找树根

后序遍历逆序 "左右根"变为"根右左"

因为"根"很重要 我们优先找根 进行分左右操作

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=40;
queue<int>q;//BFS输出层序遍历时用到的队列
vector<int>ans;//存层序遍历的输出
int a[N];//存后序遍历
int b[N];//存中序遍历
int idx;
int n;

struct Node
{
    int l,r;
}nodes[N];

int build(int L,int R)
{
    if(L>R)return 0;
    int root=++idx;//对应"根右左"中"根"的赋值
    
    //mid寻找该层(L~R)区间的根节点
    int mid;
    for(mid=L;mid<=R;mid++)if(b[mid]==a[idx])break;
    //build函数依照a数组的"根右左"顺序建树
    //下两行命令对应"根右左"中的"右左"
    nodes[root].r=build(mid+1,R);
    nodes[root].l=build(L,mid-1);
    
    return root;
}

void bfs(int x)
{
    q.push(x);
    
    while(q.size())
    {
        int t=q.front();
        q.pop();
        
        //空节点的idx编号为0
        if(t==0)continue;
        
        ans.push_back(t);
        //层序遍历
        //依次把左右子节点放入队列
        q.push(nodes[t].l);
        q.push(nodes[t].r);
    }
}

int main()
{
    cin>>n;
    //后序遍历是"左右根"的顺序
    //逆序读入是获得"根右左"的特殊顺序
    for(int i=n;i>=1;i--)cin>>a[i];//根右左
    for(int i=1;i<=n;i++)cin>>b[i];//中序
    
    //root获得树根的idx值
    //若root为0则为空树
    //否则root=1作为树根
    int root=build(1,n);//建树操作
    
    //层序遍历
    bfs(root);
    
    int si=ans.size();
    int cnt=0;
    for(auto it:ans)
    {
        cout<<a[it];
        cnt++;
        if(cnt<si)cout<<' ';
    }
    
    return 0;
}

PTA L2-011 玩转二叉树

已知:前序遍历 中序遍历

所求:镜像翻转后的层序遍历

所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。

解决镜像问题:交换所有节点的左右子节点 或者 BFS层序遍历时遵循"先右后左"的方式入队

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=40;
vector<int>ans;
int a[N],b[N];
//叶节点的两个空子节点(idx=0)也会入队
//所以数组模拟的队列要开大些
int q[N*N];
int idx;
int n;

struct Node
{
    int l,r;
}nodes[N];

int build(int L,int R)
{
    if(L>R)return 0;
    int root=++idx;//新的根 从1开始
    int mid;//mid是idx编号
    //在中序遍历中寻找与当前子树根节点值相同的节点编号
    //进行"分左右"的操作
    for(mid=L;mid<=R;mid++)if(a[mid]==b[root])break;
    nodes[root].l=build(L,mid-1);
    nodes[root].r=build(mid+1,R);
    
    return root;  
}

void bfs(int s)
{
    int hh=0,tt=0;
    q[0]=s;
    
    while(hh<=tt)
    {
        int t=q[hh++];
        if(t==0)continue;
        ans.push_back(t);
        q[++tt]=nodes[t].r;
        q[++tt]=nodes[t].l;
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];//中序
    for(int i=1;i<=n;i++)cin>>b[i];//前序
    
    int root=build(1,n);
    //for(int i=1;i<=idx;i++)swap(nodes[i].l,nodes[i].r);
    
    bfs(root);
    
    int cnt=0;
    int si=ans.size();
    for(auto it:ans)
    {
        cout<<b[it];
        cnt++;
        if(cnt<si)cout<<' ';
    }
    
    return 0;    
}

PTA L2-004 这是二叉搜索树吗?

已知:满足二叉搜索树 序遍历

所求:序遍历

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树,对于任一结点:

  • 其左子树中所有结点的键值小于该结点的键值;
  • 其右子树中所有结点的键值大于等于该结点的键值;
  • 其左右子树都是二叉搜索树。

所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。

解决镜像问题:二叉搜索树镜像后 左子树大 右子树小

重点思路:

在前序或后序遍历中

除了第一个或最后一个节点(根节点)

其余节点具有"二段性" 对于其中某一段 要么全部大于根节点 要么全部小于根节点

若不符合 则该二叉搜索树不合法

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5;
vector<int>ans;
int a[N],b[N];
int n,idx,k;
bool flag=true;

struct Node
{
    int l,r,val;
}nodes[N];

//正常建树
int build(int L,int R)
{
    if(!flag)return 0;
    if(L>R)return 0;
    int root=++idx;
    nodes[root].val=a[L];
    int mid=L+1;
    //在区间范围内寻找第一个大于等于根节点的值的位置 存为mid
    //若在mid之后仍然有小于根节点值的值 则非法
    while(a[mid]<a[L]&&mid<=R)mid++;
    for(int i=mid;i<=R;i++)if(a[i]<a[L]){flag=false;return root;}
    nodes[root].l=build(L+1,mid-1);
    nodes[root].r=build(mid,R);
    return root;
}

//镜像建树
int build2(int L,int R)
{
    if(!flag)return 0;
    if(L>R)return 0;
    int root=++idx;
    nodes[root].val=a[L];
    if(L==R)return root;
    int mid=L+1;
    while(a[mid]>=a[L]&&mid<=R)mid++;
    //二叉搜索树镜像后
    //左子树大 右子树小
    for(int i=mid;i<=R;i++)if(a[i]>=a[L]){flag=false;return root;}
    nodes[root].l=build2(L+1,mid-1);
    nodes[root].r=build2(mid,R);
    return root;
}

void df(int root)
{
    if(!root)return;
    //后序遍历的输出(放入vector)
    //符合"左右根"
    df(nodes[root].l);
    df(nodes[root].r);
    ans.push_back(root);
}

void dfs(int x)
{
    df(x);
    puts("YES");
    int cnt=0;
    int si=ans.size();
    for(auto it:ans)
    {
        cout<<nodes[it].val;
        cnt++;
        if(cnt<si)cout<<' ';
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    
    int root=build(1,n);
    
    if(flag){dfs(root);return 0;}
    
    flag=true;
    root=build2(1,n);
    if(flag){dfs(root);return 0;}
    
    puts("NO");
    
    return 0;
}

PTA L3-016 二叉搜索树的结构

维护节点所在层数父节点

已知:一堆节点的值

方法:使用insert函数建立二叉搜索树

#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<sstream>
using namespace std;
const int N=110;
//从值映射到对应idx编号
//当ma[i]==0时 说明值为i的点不存在
map<int,int>ma;
int idx;
struct Node
{
    int l,r,val,floor,father;
    //记录左右子节点 节点值
    //维护节点所在层数 以及父节点
}nodes[N];

//二叉搜索树的建树方式
//pos是引用传参
void insert(int &pos,int val,int floor,int father)
{
    if(pos==0)
    {
        //当pos为0时 说明找到了可以插入的位置
        //叶子节点的左右子节点的idx编号值为0
        pos=++idx;
        nodes[pos].val=val;
        nodes[pos].floor=floor;
        nodes[pos].father=father;
        ma[val]=pos;
        return;
    }
    //寻找合适的插入位置
    //大于往右 小于往左
    if(nodes[pos].val>val)insert(nodes[pos].l,val,floor+1,pos);
    else insert(nodes[pos].r,val,floor+1,pos);
}

int main()
{
    int n;
    cin>>n;
    int root=0;
    while(n--)
    {
        //tmp是节点值
        int tmp;cin>>tmp;
        insert(root,tmp,1,0);
    }
    int m;
    scanf("%d",&m);getchar();//后面要一整行地读入 吸收换行符
    while(m--)
    {
        string str;
        getline(cin,str);
        stringstream ss(str);//用str初始化ss
        str.clear();//清空str
        vector<string>v;
        while(ss>>str)v.push_back(str);//v中存一整行的字符串
        if(v.back().back()=='t')
        {
            if(ma[stoi(v[0])]==root&&ma[stoi(v[0])])puts("Yes");
            else puts("No");
        }
        else if(v.back().back()=='s')
        {
            int a=stoi(v[0]),b=stoi(v[2]);
            if(nodes[ma[a]].father==nodes[ma[b]].father&&ma[a]&&ma[b])puts("Yes");
            else puts("No");
        }
        else if(v.back().back()=='l')
        {
            int a=stoi(v[0]),b=stoi(v[2]);
            if(nodes[ma[a]].floor==nodes[ma[b]].floor&&ma[a]&&ma[b])puts("Yes");
            else puts("No");
        }
        else if(v[3][0]=='p')
        {
            int a=stoi(v[0]),b=stoi(v[5]);
            if(nodes[ma[b]].father==ma[a]&&ma[a]&&ma[b])puts("Yes");
            else puts("No");
        }
        else if(v[3][0]=='l')
        {
            int a=stoi(v[0]),b=stoi(v[6]);
            if(nodes[ma[b]].l==ma[a]&&ma[a]&&ma[b])puts("Yes");
            else puts("No"); 
        }
        else if(v[3][0]=='r')
        {
            int a=stoi(v[0]),b=stoi(v[6]);
            if(nodes[ma[b]].r==ma[a]&&ma[a]&&ma[b])puts("Yes");
            else puts("No");
        }
    }
    
    return 0;
}

PTA L2-035 完全二叉树的层序遍历

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树

完全二叉树:如果不完美 最后一层的节点全部"靠左" 最后一层以上全满

已知:完全二叉树后序遍历

所求:层序遍历

当用"x<<1"与"x<<1|1"表示左右子节点的编号建树时
顺序遍历数组就是层序遍历 不需要BFS

#include<iostream>
#include<queue>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
const int N=1e4;
int val[N];
int n;
void build(int x)
{
    //由后序遍历建树
    //依照"左右根"的顺序
    if(lc<=n)build(lc);//左
    if(rc<=n)build(rc);//右
    cin>>val[x];//根
}

void bfs(int x)
{
    queue<int>q;
    q.push(x);
    int cnt=0;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        cout<<val[t];
        cnt++;
        if(cnt!=n)cout<<' ';
        //不能遍历到超出总节点个数的idx编号
        if((t*2)<=n)q.push(t<<1);
        if((t*2+1)<=n)q.push(t<<1|1);
    }
}
int main()
{
    cin>>n;
    build(1);
    //bfs(1);
    //当用x<<1 x<<1|1 表示左右子节点时
    //顺序遍历数组就是层序遍历 不需要bfs
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        cout<<val[i];
        cnt++;
        if(cnt!=n)cout<<' ';
    }
    return 0;
}

PTA L3-010 是否完全二叉搜索树

本题的特殊定义 二叉搜索树中 (左子树键值大 右子树键值小)

题意:按照二叉搜索树的要求插入节点 判断是否是完全二叉树 并给出层序遍历

判断技巧:

如果有一个节点的idx编号超过节点数

一定是不完全的

可以理解为前面少了一个节点补到的后面 如果全满就不会有超过n(节点数)的编号

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=4e5;
struct Node
{
    //记录数值 二叉树中的编号
    //符合x<<1 x<<1|1 的规则
    int l,r,val,id;  
}nodes[N];
int n;
int idx;
int id;
//pos是引用传参
void insert(int& pos,int val)
{
    //当pos为0时 说明找到了可以插入的位置
    //叶子节点的左右子节点的idx编号值为0
    if(pos==0)
    {
        pos=++idx;
        nodes[pos].val=val;
        nodes[pos].id=id;
        return;
    }
    if(val>nodes[pos].val)
    {
        id=id*2;
        insert(nodes[pos].l,val);
    }
    else
    {   
        id=id*2+1;
        insert(nodes[pos].r,val);
    }
}
int main()
{
    int root=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int tmp;cin>>tmp;
        //每次插入id从1开始
        //往左*2 往右*2+1
        id=1;
        insert(root,tmp);
    }
    queue<int>q;
    q.push(root);
    bool flag=true;
    int cnt=0;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        if(!t)continue;
        //如果idx值大于总节点数 一定不合法
        if(nodes[t].id>n)flag=false;
        cout<<nodes[t].val;
        cnt++;if(cnt!=n)cout<<' ';
        q.push(nodes[t].l);
        q.push(nodes[t].r);
    }
    puts("");
    if(flag)puts("YES");
    else puts("NO");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没伞的男孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值