问题 D: 上帝视角

问题 D: 上帝视角

时间限制: 1 Sec
内存限制: 128 MB
提交: 581
解决: 150

题目描述

给一棵二叉树的层序遍历序列和中序遍历序列,求这棵二叉树的先序遍历序列和后序遍历序列,并给出从右往左、从右上往左下、从上往下分别能看到的结点个数。注意,此处均把二叉树的每条边都设置为等长,角度为45度,因此结点可能在视觉上重叠。所谓从右往左看是指,对同一层的结点,右边的结点会挡住左边的结点,这样同一层结点就只能看到最右边的那一个;同样的,从右上往左下看是指,右上角的结点会挡住左下角45度的结点;从上往下看是指,对同一竖直位置来说,只能看到最上方的结点。

例如对下图来说,从右往左能看到3个结点,从右上往左下能看到3个结点,从上往下能看到5个结点。

输入

每个输入文件中一组数据。

第一行一个正整数N(1<=N<=30),代表二叉树的结点个数(结点编号为1~N)。

接下来两行,每行N个正整数,分别代表二叉树的层序遍历序列和中序遍历序列。数据保证序列中1~N的每个数出现且只出现一次。

输出

先输出两行,每行N个正整数,分别代表二叉树的先序遍历序列和后序遍历序列。

接下来分三行输出从右往左、从右上往左下、从上往下分别能看到的结点个数。

每行末尾均不允许输出多余的空格。

样例输入

7
1 2 3 4 5 6 7
4 2 5 1 6 3 7

样例输出

1 2 4 5 3 6 7
4 5 2 6 7 3 1
3
3
5

提示

Part One:
就是模拟赛(1)中的 C 题。
在先序+中序中,先序的第一个结点是根结点,然后用中序区分出左右子树。到这里为止,
层序+中序也是一样的,层序的第一个结点是根结点,然后用中序区分出左右子树。
不同在于后面的步骤。
由于先序中是按照“根结点-左子树所有结点-右子树所有结点”的顺序的,因此可以直接
对左子树跟右子树进行递归,但是层序不行,层序的左右子树结点在层序序列中可能是分散的。
因此必须想办法把左子树所有结点跟右子树所有结点都找出来。
于是可以开两个 vector,一个存左子树所有结点的层序,一个存右子树所有结点的层序。
然后遍历当前层序序列的所有结点,根据中序序列判断其属于左子树还是右子树,如果是左子
树就 push_back 到左子树的 vector,否则就 push_back 到右子树的 vector。
这样就可以继续递归了,递归参数中用 vector 表示层序,而中序可以仍然用数组下标。
Part Two:
从右往左看,其实就是求树的高度。
从右上往左下看,我们会发现,左孩子总是被根结点挡住。所以可以开一个计数变量,从
根节点开始遍历这棵树,当往左子树前进时,计数变量不变;当往右子树前进时,计数变量加
1。过程中计数变量能够达到的最大值就是我们要求的解。
从上往下看,可以发现,根结点左侧能看到的结点个数等于从根结点开始进入左子树之后,
从左前进的次数减去从右前进的次数;根结点右侧能看到的结点个数等于从根结点开始进入右
子树之后,从右前进的次数减去从左前进的次数。因此不妨开一个计数变量,从根结点开始遍
历这棵树,当往左子树前进时,计数变量减 1;当往右子树前进时,计数变量加 1。过程中计
数变量能够到达的最小负值就是根结点左侧能看到的结点个数;能达到的最大正值就是根结点
右侧能看到的结点个数。将以上两个绝对值相加后加 1 就是从上往下能看到的结点。

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 50;
const int INF = 0x3fffffff;
struct node {
    int data;
    int lchild;
    int rchild;
}Node[maxn];
int in[maxn];    //中序
vector<int> pre, post;
int num = 0;    // 已分配的结点个数
int newNode(int x) {    // 分配结点
    Node[num].data = x;
    Node[num].lchild = Node[num].rchild = -1;    // -1表示NULL
    return num++;
}
//当前二叉树的层序序列为layer,中序序列区间为[inL, inR]
//create函数返回构建出的二叉树的根结点地址
int create(vector<int> layer, int inL, int inR) {
    if(layer.size() == 0) {
        return -1;
    }
    int root = newNode(layer[0]);  //新建一个新的结点,用来存放当前二叉树的根结点
    int k;
    for(k = inL; k <= inR; k++) {    // 在中序中找根结点
        if(in[k] == layer[0]) {
            break;
        }
    }
    vector<int> layerLeft;    // 左子树层序
    vector<int> layerRight;    // 右右子树层序
    for(int i = 1; i < layer.size(); i++) {    // 剩余所有元素
        bool isLeft = false;    // 是否在左子树
        for(int j = inL; j < k; j++) {    // 在中序中找layer[i]
            if(layer[i] == in[j]) {    // 找到layer[i]
                isLeft = true;    // layer[i]在左子树
                break;
            }
        }
        if(isLeft) layerLeft.push_back(layer[i]);    // 如果layer[i]在左子树,那么加入左子树层序
        else layerRight.push_back(layer[i]);    // 如果layer[i]在右子树,那么加入右子树层序
    }
    Node[root].lchild = create(layerLeft, inL, k - 1);    // 处理左子树
    Node[root].rchild = create(layerRight, k + 1, inR);    // 处理右子树
    return root;  //返回根结点地址
}
void preOrder(int root) {    // 先序
    if(root == -1) {
        return;
    }
    pre.push_back(Node[root].data);
    preOrder(Node[root].lchild);
    preOrder(Node[root].rchild);
}
void postOrder(int root) {    // 后序
    if(root == -1) {
        return;
    }
    postOrder(Node[root].lchild);
    postOrder(Node[root].rchild);
    post.push_back(Node[root].data);
}
int ans1 = 0;    // 从右往左:深度
void rightToLeftDFS(int root, int depth) {
    if(root == -1) {
        return;
    }
    if(depth > ans1) ans1 = depth;    // 更新ans1
    rightToLeftDFS(Node[root].lchild, depth + 1);    // 左孩子跟右孩子的深度都加1
    rightToLeftDFS(Node[root].rchild, depth + 1);
}
int ans2 = 0;    // 从右上往左下:往右的次数
void rightupToLeftdownDFS(int root, int right) {
    if(root == -1) {
        return;
    }
    if(right > ans2) ans2 = right;    // 更新ans2
    rightupToLeftdownDFS(Node[root].lchild, right);    // 往左时计数变量right不变
    rightupToLeftdownDFS(Node[root].rchild, right + 1);    // 往右时计数变量right加1
}
int minLeft = 0, maxRight = 0;    // 从上往下:最小负值,最大正值
void upToDownDFS(int root, int balance) {
    if(root == -1) {
        return;
    }
    if(balance > maxRight) maxRight = balance;    // 更新maxRight
    if(balance < minLeft) minLeft = balance;    // 更新minLeft
    upToDownDFS(Node[root].lchild, balance - 1);    // 往左时计数变量balance减1
    upToDownDFS(Node[root].rchild, balance + 1);    // 往右时计数变量balance加1
}
int main() {
    vector<int> layer;
    int n, temp;
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%d", &temp);
        layer.push_back(temp);    // 程序
    }
    for(int i = 0; i < n; i++) {
        scanf("%d", &in[i]);    // 中序
    }
    int root = create(layer, 0, n - 1);    //建树
    preOrder(root);    // 先序
    postOrder(root);    // 后序
    for(int i = 0; i < n; i++) {
        printf("%d", pre[i]);
        if(i < n - 1) printf(" ");
        else printf("\n");
    }
    for(int i = 0; i < n; i++) {
        printf("%d", post[i]);
        if(i < n - 1) printf(" ");
        else printf("\n");
    }
    rightToLeftDFS(root, 1);    // 从右往左,根结点深度为1
    rightupToLeftdownDFS(root, 1);    // 从右上往左下,根节点为第一个结点
    upToDownDFS(root, 0);    // 从上往下,根结点先不算在内
    printf("%d\n%d\n%d\n", ans1, ans2, maxRight - minLeft + 1);    // 三个解
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值