个人练习-PAT甲级-1119 Pre- and Post-order Traversals

题目链接https://pintia.cn/problem-sets/994805342720868352/problems/994805353470869504

题目大意:给出一棵二叉树的前序和后序遍历,如果这棵树是唯一的,输出Yes并给出其中序遍历;否则输出No,并给出其中一种可能的中序遍历。

思路:思路其实是有的,就是递归来做。根确定后,找出左子树和右子树(的范围),对其继续递归。难点在于如何判断唯一or不唯一。看了一些解答后明白了:只要某一个结点只有左儿子or只有右儿子,那它就是不唯一的。因为这时这个儿子是挂在左边还是挂在右边无法确定。

那么就可以开始了。首先确定先序数组的范围[s1, t1]和后序数组的范围[s2, t2]。开始时pre[s1]==pos[t2],先序的开头等于后序的末尾,即根节点。

随后,假定pre[s1+1]是属于根的左儿子,即左子树的根(就算是右子树也无所谓,之后还会做判断),用一个指针ptrpos[t2-1]开始向前遍历,直到找到元素与根的左儿子pre[s1+1]相等为止。

那么接下来从ptr包括它自己一直向左到s2都是左子树;从ptr不包括它自己向右一直到t2-1都是右子树。注意ptr是在pos[]数组内的指针,所以刚才讲的这个左右子树范围是在pos[]数组内的。通过这个上下限我们可以得到左右子树的size,所以很容易可以得到左右子树在pre[]数组内的范围。

接下来判断是否unique。前面假定了pre[s1+1]为根的左儿子,那么如果pre[s1+1]不是左儿子,而是右儿子,那么说明根只有右儿子,即树不unique了。这里t2-ptr-1为右子树的size

        if (t2 - ptr == 1)
            isUnique = false;

可能你会问,那么【左子树的size为0的情况怎么判断?】问得好!因为我们已经先假定pre[s1+1]为根的左儿子了,那么如果【左子树size为0】,那么实际上这棵树是不确定的,而不确定,意味着它挂在左边挂在右边都可以!所以我们的假定实际上是规定了【单个儿子都默认挂在左边】,因此非叶子结点不会出现【左子树size为0】的情况。

实际上柳神的写法和我相反,她是默认【单个儿子都默认挂在右边】。按照题意,任意一个单儿子,挂在左边还是挂在右边都行。所以理论上讲可以设置50%几率挂在左边、50%几率在右边。但看样例是默认挂在右边的,如果有些挂左有些挂右,不知道OJ会怎么判断。。。

因为要输出的是中序,因此注意遍历顺序

        getInorder(s1 + 1, s1 + 1 + ptr - s2, s2, ptr);
        in.push_back(pre[s1]);
        getInorder(t1 - (t2 - ptr - 2), t1, ptr + 1, t2 - 1);

完整代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <set>


using namespace std;


int N;
bool isUnique = true;
vector<int> pre, pos, in;


void getInorder(int s1, int t1, int s2, int t2) {
    if (s1 == t1 || s2 == t2) {
        in.push_back(pre[s1]);
        return;
    }
    if (pre[s1] == pos[t2]) {
        int ptr = t1 - 1;
        while (ptr >= s2) {
            if (pos[ptr] == pre[s1 + 1])
                break;
            ptr--;
        }

        if (t2 - ptr == 1)
            isUnique = false;

        getInorder(s1 + 1, s1 + 1 + ptr - s2, s2, ptr);
        in.push_back(pre[s1]);
        getInorder(t1 - (t2 - ptr - 2), t1, ptr + 1, t2 - 1);
    }
}


int main() {
    scanf("%d", &N);
    pre.resize(N);
    pos.resize(N);
    for (int i = 0; i < N; i++)
        scanf("%d", &pre[i]);
    for (int i = 0; i < N; i++)
        scanf("%d", &pos[i]);

    int s1 = 0, t1 = N-1, s2 = 0, t2 = N-1;

    getInorder(s1, t1, s2, t2);
    if (isUnique)
        printf("Yes\n");
    else
        printf("No\n");

    printf("%d", in[0]);
    for (int i = 1; i < in.size(); i++)
        printf(" %d", in[i]);
    printf("\n");


    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值