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;
}