洛谷题单指南-二叉树-P1185 绘制二叉树
原题链接:https://www.luogu.com.cn/problem/P1185
题意解读:在表格中绘制二叉树,有几个关键点
1、结点用小写字母 o
表示,对于一个父亲结点,用 /
连接左子树,用 \
连接右子树,表格其余地方填空格。
2、第 层结点若两个属于同一个父亲,那么它们之间由 3 个空格隔开;若两个结点相邻但不属于同一个父亲,那么它们之间由1个空格隔开,据此可以计算第m层的宽度。
解题思路:
1、初始化画布大小
用二维数组代表画布,初始全部都是空格
char graph[800][2000];
画布的高、宽如何确定,结合题意,观察图形:
m = 2时,
o
/ \
o o
高度 = 3,宽度 = 5
m = 3时,
o
/ \
/ \
o o
/ \ / \
o o o o
高度 = 6, 宽度 = 5 * 2 + 2 - 1 (最底层一对节点宽度是5,一共有2对,2对之间有2 - 1个空格)
m = 4时,
o
/ \
/ \
/ \
/ \
/ \
o o
/ \ / \
/ \ / \
o o o o
/ \ / \ / \ / \
o o o o o o o o
高度 = 12, 宽度 = 5 * 4 + 4 - 1最底层一对节点宽度是5,一共有4对,4对之间有4 - 1个空格)
根据上述分析,可以推出二叉树画布的高度 = 3 * 2 ^ (m - 2),宽度 = 5 * 2 ^ (m - 2) + 2 ^ (m - 2) - 1
2、递归画图
先计算二叉树的根节点位置,从根节点开始,递归画图
画图函数如下:
void draw(int x, int y, int depth, int i, int j)
x, y表示起始节点,depth表示起始节点到左右相连的树枝的高度,i,j表示该节点是第i层第j个:
接下来调用
w = 5 * pow(2, m - 2) + pow(2, m - 2) - 1; //二叉树底部宽度
h = 3 * pow(2, m - 2); //二叉树高度
draw(1, w / 2 + 1, (h + 1) / 2, 1, 1); //根节点在第一行正中间
x = 1, y = w / 2 + 1表示第一行的正中间,是根节点所在位置
depth = (h + 1) / 2表示要画的x、y与其相连的树枝的总高度,如m = 4时,表示要画这样的图形:
o
/ \
/ \
/ \
/ \
/ \
画完根节点以及与之相连的树枝,再计算左子结点的位置、右子节点的位置,递归画图。
3、删除节点
如何实现节点删除呢?
可以用二维数组保存删除的节点
bool del[20][600];
del[i][j] = true表示第i层第j个节点被删除
在上述draw函数中,已经记录了每个节点是第几层第几个,只需要再画(x、y)节点之后,判断左、右子节点是否被删除,如果被删除则树枝和相应递归就不再继续画,就实现了删除的功能。
100分代码:
#include <bits/stdc++.h>
using namespace std;
char graph[800][2000]; //画布
bool del[20][600]; //被删除的节点
int m, n, w, h;
//从root开始,画高度是depth的节点和树枝,节点是第i层第j个
void draw(int x, int y, int depth, int i, int j)
{
//画节点
graph[x][y] = 'o';
if(i == m) return; //最后一层
int ileft = i + 1, jleft = j * 2 - 1; //左子结点是第ilfet层、第jleft个
int iright = i + 1, jright = j * 2; //右子节点是第iright层,第jright个
//画树枝
for(int i = 1; i < depth; i++)
{
if(!del[ileft][jleft]) graph[x + i][y - i] = '/'; //左边树枝,如果左子节点被删除,不画树枝
if(!del[iright][jright]) graph[x + i][y + i] = '\\'; //右边,如果右子节点被删除,不画树枝
}
if(!del[ileft][jleft]) draw(x + depth, y - depth, (depth + 1) / 2, ileft, jleft); //如果左子节点未删除,递归画左子树
if(!del[iright][jright]) draw(x + depth, y + depth, (depth + 1) / 2, iright, jright); //如果右子节点未删除,递归画右子树
}
void show()
{
for(int i = 1; i <= h; i++)
{
for(int j = 1; j <= w; j++)
{
cout << graph[i][j];
}
cout << endl;
}
}
int main()
{
memset(graph, ' ', sizeof(graph));
cin >> m >> n;
int x, y;
while(n--)
{
cin >> x >> y;
del[x][y] = true;
}
w = 5 * pow(2, m - 2) + pow(2, m - 2) - 1; //二叉树底部宽度
h = 3 * pow(2, m - 2); //二叉树高度
draw(1, w / 2 + 1, (h + 1) / 2, 1, 1); //根节点在第一行正中间
show();
return 0;
}