洛谷题单指南-二叉树-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;
}

 

posted @ 2024-03-19 11:50  五月江城  阅读(87)  评论(0编辑  收藏  举报