P1185 绘制二叉树——蒟蒻的暴力模拟

被此题逼疯,必须发博庆祝


前言

一上午自习然而被一题卡脖子。

建树、删树……行行行,你让我做什么我就做什么,无力吐槽模拟。


一、题目

题目描述

二叉树是一种基本的数据结构,它要么为空,要么由根节点,左子树和右子树组成,同时左子树和右子树也分别是二叉树。

当一颗二叉树高度为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++; 
	}
}

总结

花了一个上午,尤其是被节点的坐标弄晕了,与其狠狠模拟,实则有一些更好的方法。看看他人的题解,应该会有更多收获。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值