PAT 1119 Pre- and Post-order Traversals

根据树的先序遍历和后序遍历得到中序遍历,如果树是唯一输出Yes,否则输出No。然后输出中序遍历(如果不唯一,随便输出那一棵)。
题目读起来,写起来不轻松。我主要将树分为三种情况:
1、有一棵子树,则树是不唯一,默认为左子树。
2、有左右子树
3、没有子树,为叶子节点。
这一题难点跟以前类似的题目一样,要把边界写对。我是根据先序在后序的上位置进行匹配,然后计算中序的下标,一个个对应赋值,还要计算中序遍历的下标,难度又增加,短时间内是很难写出来。
成功通过后,看了一下别人的思路,发现写的是又简洁又清晰,是根据先序和后序,对树进行遍历,直接用队列存储值,这样就省去了求中序下标的麻烦。根据别人的思路,我对自己的代码进行了小优化。
事后想想,自己之所以这么写,也是自己对树的遍历理解不够,在写的过程是边写代码边写注释理思路,树的先序遍历和后序遍历的一些特性没有掌握。以前知道先序和后序不能得到唯一棵树,但不知道为什么也没有去深究,今天这一题算是让我对这明白了一些。
(用时:3:29:30.59)

#include <bits/stdc++.h>
using namespace std;
bool flag = true;
int t=0;
//根据别人的思路,对自己的代码进行优化
void GetMidOrder1(int preOrder[],int postOrder[],int preIndex,int preRight,int postLeft,int postRight,int midOrder[])
{
    if(postLeft>=postRight) {
        return;
    }
    //先序遍历中左节点位置
    int preleftIndex = preIndex +1;
    //当前节点后序遍历中的位置
    int postIndex = postRight - 1;

    //后序遍历中左节点的位置
    int postleftIndex = -1;
    //有子节点
    if(preleftIndex<preRight ) {
        for(int i=postLeft; i<postRight; i++) {
            if(preOrder[preleftIndex] == postOrder[i]) {
                postleftIndex = i;
                break;
            }
        }
        //右子树的节点数
        int rightNum = 0;
        //只有一个子节点 当做是左子树
        if(postleftIndex==postIndex-1 ){
        //只有一棵子树,不能确定是左还是右
        flag = false;
        GetMidOrder1(preOrder,postOrder,preleftIndex,preRight,postLeft,postIndex,midOrder);
         midOrder[t++] = preOrder[preIndex];
        }
        //有左右子树
        else{
           //右子树的节点个数
           rightNum = postIndex-postleftIndex -1;
           //后序遍历的根的右节点在根节点的左边一位
           int postRightIndex = postIndex - 1;
           //先序遍历上的右节点的位置
           int preRightIndex = -1;
          for(int i=preIndex +2; i<preRight; i++) {
            if(postOrder[postRightIndex] == preOrder[i]) {
                preRightIndex = i;
                break;
            }
        }
        //左子树                       后序遍历范围是左边界 到  根节点减去右子树的节点数
         GetMidOrder1(preOrder,postOrder,preleftIndex,preRightIndex, postLeft,postIndex -rightNum ,midOrder);
               midOrder[t++] = preOrder[preIndex];
         //右子树的后序遍历范围是根节点减去右子树的节点数 到 根节点
         //isleft+1 右子树在中序遍历中往右移
         GetMidOrder1(preOrder,postOrder,preRightIndex,preRight,postIndex-rightNum,postIndex,midOrder);
        }
    }
    else{
     midOrder[t++] = preOrder[preIndex];
    }
}

void GetMidOrder(int preOrder[],int postOrder[],int preIndex,int preRight,int postLeft,int postRight,int midOrder[],int isleft)
{
    if(postLeft>=postRight) {
        return;
    }
    //先序遍历中左节点位置
    int preleftIndex = preIndex +1;
    //当前节点后序遍历中的位置
    int postIndex = postRight - 1;

    //后序遍历中左节点的位置
    int postleftIndex = -1;
    //有子节点
    if(preleftIndex<preRight ) {
        for(int i=postLeft; i<postRight; i++) {
            if(preOrder[preleftIndex] == postOrder[i]) {
                postleftIndex = i;
                break;
            }
        }

        int rightNum = 0;
        //只有一个子节点 当做是左子树
        if(postleftIndex==postIndex-1 ){
        //只有一棵子树,不能确定是左还是右
        flag = false;
        GetMidOrder(preOrder,postOrder,preleftIndex,preRight,postLeft,postIndex,midOrder,isleft);
        }
        //有左右子树
        else{
           //右子树的节点个数
           rightNum = postIndex-postleftIndex -1;
           //后序遍历的根的右节点在根节点的左边一位
           int postRightIndex = postIndex - 1;
           //先序遍历上的右节点的位置
           int preRightIndex = -1;
          for(int i=preIndex +2; i<preRight; i++) {
            if(postOrder[postRightIndex] == preOrder[i]) {
                preRightIndex = i;
                break;
            }
        }
        //左子树                       后序遍历范围是左边界 到  根节点减去右子树的节点数
         GetMidOrder(preOrder,postOrder,preleftIndex,preRightIndex, postLeft,postIndex -rightNum ,midOrder,isleft);
         //右子树的后序遍历范围是根节点减去右子树的节点数 到 根节点
         //isleft+1 右子树在中序遍历中往右移
         GetMidOrder(preOrder,postOrder,preRightIndex,preRight,postIndex-rightNum,postIndex,midOrder,isleft+1 );
        }
        //后序遍历的左子树与中序遍历的在范围大小上一致,根的右边的节点在中序遍历的范围要向右移一个
        midOrder[postleftIndex + 1 + isleft] = preOrder[preIndex];
    }
    else{
     midOrder[postLeft + isleft] = preOrder[preIndex];
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    int preOrder[n];
    int postOrder[n];
    for(int i=0; i<n; i++) {
        scanf("%d",&preOrder[i]);
    }
    for(int i=0; i<n; i++) {
        scanf("%d",&postOrder[i]);
    }
    int midOrder[n];

    GetMidOrder1(preOrder,postOrder,0,n,0,n,midOrder);
    if(flag){
        printf("Yes\n");
    }
    else{
       printf("No\n");
    }
    for(int i=0; i<n; i++) {
        if(i==0) {
            printf("%d",midOrder[i]);
        } else {
            printf(" %d",midOrder[i]);
        }
    }

 printf("\n");

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值