被此题逼疯,必须发博庆祝
前言
一上午自习然而被一题卡脖子。
建树、删树……行行行,你让我做什么我就做什么,无力吐槽模拟。
一、题目
题目描述
二叉树是一种基本的数据结构,它要么为空,要么由根节点,左子树和右子树组成,同时左子树和右子树也分别是二叉树。
当一颗二叉树高度为m-1时,则共有m层。除m层外,其他各层的结点数都达到最大,且结点节点都在第m层时,这就是一个满二叉树。
现在,需要你用程序来绘制一棵二叉树,它由一颗满二叉树去掉若干结点而成。对于一颗满二叉树,我们需要按照以下要求绘制:
最后需要在一颗绘制好的满二叉树上删除n个结点(包括它的左右子树,以及与父亲的连接),原有的字符用空格替换(
ASCII 32
,请注意空格与ASCII 0
的区别(若用记事本打开看起来是一样的,但是评测时会被算作错误答案!))。输入格式
第1行包含2个正整数m和n,为需要绘制的二叉树层数已经从m层满二叉树中删除的结点数。
接下来n行,每行两个正整数,表示第ii层第jj个结点需要被删除
输出格式
按照题目要求绘制的二叉树。
输入输出样例
输入 #1
2 0输出 #1
o / \ o o输入 #2
4 0输出 #2
o / \ / \ / \ / \ / \ o o / \ / \ / \ / \ o o o o / \ / \ / \ / \ o o o o o o o o输入 #3
4 3 3 2 4 1 3 4输出 #3
o / \ / \ / \ / \ / \ o o / / / / o o \ / \ o o o说明/提示
二、解题分析
1.操作分析
(1)满二叉树
由于满二叉树的性质,我们可以很容易从树根建立整个树而没有歧义。
即,给定树根位置坐标,我们就可以(根据规则)画出整个二叉树。
为此我封装了一个函数,给定树的规模(深度),确定树根位置,从而建立整棵树。
(2)建立满二叉树
每一个节点都引申出另外两个节点,对于新的儿子节点,又可以引申出新的孙子节点......
每一个过程都在解决从一个点建立新的两个点的问题,因此我们可以递归地解决整个问题
void mkFBTree(int root_x,int root_y,int h,char Tree[][1600])//h表示树的节点层数
{
Tree[root_x][root_y]='o';
node[m-h+1]=root_x;
if(h==1)return;
int times=3*(1<<(h-2))/2-1;
if(h==2)times=1;
for(int i=1;i<=times;++i)
{
Tree[root_x+i][root_y-i]='/';
}
for(int i=1;i<=times;++i)
{
Tree[root_x+i][root_y+i]='\\';
}
mkFBTree(root_x+times+1,root_y-times-1,h-1,Tree);
mkFBTree(root_x+times+1,root_y+times+1,h-1,Tree);//解决左右子树
}
//函数调试完成
(3)删除子树
与(2)类似的,删除一个节点,我们就要删除他的两个(或一个或没有)儿子节点,同样可以用递归来解决。
别忘了子树的根节点与原来的整棵树还有着树枝相连哦~也要裁剪掉这些树枝。
void DeleteSubTree(int x,int y,char Tree[][1600])
{
Tree[x][y]='@';//是为了之后得到删除子树根坐标而特意的标记
int i=1;
while(Tree[x+i][y-i]=='/')
{
Tree[x+i][y-i]=' ';
i++;
}
if(Tree[x+i][y-i]=='o')
{
DeleteSubTree(x+i,y-i,Tree);
}
i=1;
while(Tree[x+i][y+i]=='\\')
{
Tree[x+i][y+i]=' ';
i++;
}
if(Tree[x+i][y+i]=='o')
{
Tree[x+i][y+i]=' ';
DeleteSubTree(x+i,y+i,Tree);
}
}
2.作为一个蒟蒻的难点分析
(1)节点之间的斜杠反斜杠数量???
没有带纸和笔所以苦苦冥思,不求通用的解析规律
int times=3*(1<<(h-2))/2-1;
if(h==2)times=1;
发现和2的倍数有关,且最初是“3个空格隔开”,然而最后一层又不满足,那就特判。
对我来说太恶心了,事实上数据不大可以直接求出存放在一个数组里
(2)确定删除节点的位置?
整棵树都存放在二维数组中,求第几层第几个节点真的很烦欸,再加上两个节点之间的连接符的长度又要算,整个人都不好了。
几经斜杠数量的摧残虐杀,我决定了,不解析得到坐标。这轻松了很多。
某个节点的x坐标:同一深度(层次)的节点,x坐标一致(设纵向为x),所以在最初建立树的时候就存储某一深度的x坐标。届时直接调取就好。
void mkFBTree(int root_x,int root_y,int h,char Tree[][1600])//h表示树的节点层数
{
...
node[m-h+1]=root_x;
...
}
int xAddress(int x)
{
return node[x];
}
但是同一层次节点的y坐标不同怎么办?怎么算出来?——都说了算死我了,拒绝解析,直接枚举。需要第n个,那我就返回第n个的坐标。
int yAddress(int x,int num)
{
int cnt=0;
for(int i=0;i!=10000;++i)
{
if(Tree[x][i]=='o'||Tree[x][i]=='@')cnt++;//如果前面不设为‘@’,那么遍历可能就会丢失一部分‘o',就不是原来顺序的编号了
if(cnt==num)return i;
}
}
三、源代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<limits.h>
#include<stack>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
int n,m;
int node[15];
char Tree[800][1600];//初始化?
void mkFBTree(int,int,int,char[][1600]);//子树根坐标及子树高度
void DeleteSubTree(int,int,char[][1600]);
void DeleteUpSlash(int,int,char[][1600]);
int xAddress(int);
int yAddress(int,int);
int main()
{
IOS;
cin>>m>>n;
int width=pow(2,m-1)/2;
width=width*5+width-1;//树的宽度
int height=(width+1)/2;
memset(Tree,' ',sizeof(Tree));
mkFBTree(1-1,(width+1)/2-1,m,Tree);
//满二叉树建立完成
int x,y;
for(int i=1;i<=n;++i)
{
cin>>x>>y;
int xx,yy;
xx=xAddress(x);
yy=yAddress(xx,y);
DeleteSubTree(xx,yy,Tree);//删除子树
DeleteUpSlash(xx,yy,Tree);
}
for(int i=0;i<height;++i)
{
for(int j=0;j<width;++j)
{
if(Tree[i][j]=='@')Tree[i][j]=' ';
}
Tree[i][width]='\0';
cout<<Tree[i]<<endl;
}
return 0;
}
void mkFBTree(int root_x,int root_y,int h,char Tree[][1600])//h表示树的节点层数
{
Tree[root_x][root_y]='o';
node[m-h+1]=root_x;
if(h==1)return;
int times=3*(1<<(h-2))/2-1;
if(h==2)times=1;
for(int i=1;i<=times;++i)
{
Tree[root_x+i][root_y-i]='/';
}
for(int i=1;i<=times;++i)
{
Tree[root_x+i][root_y+i]='\\';
}
mkFBTree(root_x+times+1,root_y-times-1,h-1,Tree);
mkFBTree(root_x+times+1,root_y+times+1,h-1,Tree);
}
//函数调试完成
void DeleteSubTree(int x,int y,char Tree[][1600])
{
Tree[x][y]='@';
int i=1;
while(Tree[x+i][y-i]=='/')
{
Tree[x+i][y-i]=' ';
i++;
}
if(Tree[x+i][y-i]=='o')
{
DeleteSubTree(x+i,y-i,Tree);
}
i=1;
while(Tree[x+i][y+i]=='\\')
{
Tree[x+i][y+i]=' ';
i++;
}
if(Tree[x+i][y+i]=='o')
{
Tree[x+i][y+i]=' ';
DeleteSubTree(x+i,y+i,Tree);
}
}
int xAddress(int x)
{
return node[x];
}
int yAddress(int x,int num)
{
int cnt=0;
for(int i=0;i!=10000;++i)
{
if(Tree[x][i]=='o'||Tree[x][i]=='@')cnt++;
if(cnt==num)return i;
}
}
void DeleteUpSlash(int x,int y,char Tree[][1600])
{
int i=1;
while(Tree[x-i][y-i]=='\\')
{
Tree[x-i][y-i]=' ';
i++;
}
i=1;
while(Tree[x-i][y+i]=='/')
{
Tree[x-i][y+i]=' ';
i++;
}
}
总结
花了一个上午,尤其是被节点的坐标弄晕了,与其狠狠模拟,实则有一些更好的方法。看看他人的题解,应该会有更多收获。