实验课--E-二叉树的中后序遍历构建及求叶子

二叉树的中后序遍历构建及求叶子

写完这道题之后,你对树的理解应该会更加深刻

题目描述

按中序遍历和后序遍历给出一棵二叉树,求这棵二叉树中叶子节点权值的最小值。

输入保证叶子节点的权值各不相同。

输入

第一行输入一个整数t,表示有t组测试数据。

对于每组测试数据,首先输入一个整数N (1 <= N <= 10000),代表二叉树有N个节点,接下来的一行输入这棵二叉树中序遍历的结果,最后一行输入这棵二叉树后序遍历的结果。

输出

对于每组测试数据,输出一个整数,代表二叉树中叶子节点权值最小值。

样例输入

3
7
3 2 1 4 5 7 6
3 1 2 5 6 7 4
8
7 8 11 3 5 16 12 18
8 3 11 7 16 18 12 5
1
255
255

样例输出

1
3
255

题解

如果你急着要代码,可以先把代码复制了,但是main函数请自己写哈哈哈哈哈哈哈哈哈哈
如果你不急,那么请听我细细道来

#include <iostream>
#include <cstdio>
#include <algorithm>
 
using namespace std;
const int maxn = 1e6;
int post[maxn];
int ino[maxn];
int n, ans;
int cnt;
 
void init()
{
    ans = 0x7fffffff;
    cin >> n;
    for (int i = 0; i < n; i++)
        scanf("%d", ino + i);
    for (int i = 0; i < n; i++)
        scanf("%d", post + i);
}
 
int find(int l,int r,int num)
{
    for (int i = l; i < r; i++)
    {
        if (num == ino[i])
            return i;
    }
    return -1;
}
 
void solve(int l, int r, int root)
{
    if (r <= l)
        return;
    if (l + 1 == r)
    {
        ans = min(ans, ino[l]);
        return;
    }
    int mid = find(l,r,post[root]);
    solve(mid + 1, r, root - 1);
    solve(l, mid, root-(r-mid));
}
 
int main()
{
    // 请自己写
    return 0;
}

题目的只给了我们中序遍历和后序遍历的树,让我们通过这两个遍历找出所有叶子节点
对此我们先来讲讲建树

中序和后序建树

现在有一棵树,它长这样

1
2
3

那么这颗树的
中序遍历是这样213
后序遍历是这样231

对这颗树拓展一下就是

对所有可能的树
中序遍历是这样(左子树)(根节点)(右子树)
后序遍历是这样(左子树)(右子树)(根节点)

在这里插入图片描述
由此,我们可以发现,一颗树的后序遍历的最后一个节点,必定是那颗树的根节点root

然后我们可以通过那个根节点,在中序遍历里找到根节点的位置mid

这样左子树在中序遍历中的位置就是[l,mid-1]

右子树在中序遍历中的位置就是[mid+1,r]

对于这个在中序遍历里找到根节点位置,我们写了一个函数

int find(int l,int r,int num)
{
	// l 就是中序遍历的那棵树的左边边界
	// r 就是中序遍历的那棵树的右边边界
    for (int i = l; i < r; i++)
    {
        if (num == ino[i])
            return i;
    }
    return -1;
}

但是我们找到了中序遍历的左子树和右子树又有什么用呢?

我们来回顾一下题目要我们求什么,求叶子节点的最小值,也就是说找到所有叶子节点

什么是叶子节点?没有左子树,没有右子树!

那么那棵树应该长这样

root
NULL
NULL
void solve(int l, int r, int root)
{
	// 要理清这三个参数的含义
	// l,r 是针对中序遍历的
	// 在[l,r) 中找一颗树
	// root 是针对后序遍历的,存储的是后序遍历中当前这棵树的最后一个节点
    if (r <= l) // 不合法的树
        return;
    if (l + 1 == r) // 树里只有一个根节点,那么就是叶子
    {
        ans = min(ans, ino[l]); // 更新答案,找到最小的叶子
        return;
    }
    // 树里不止一个节点
    int mid = find(l,r,post[root]); // 找到根节点在中序遍历中的位置
    solve(mid + 1, r, root - 1); // 找右子树
    solve(l, mid, root-(r-mid)); // 找左子树
}

可能有的同学会问,为什么是root-1?为什么是root-(r-mid)?
在这里插入图片描述
root 代表的是当前树在后序遍历中的最后一位
所以对于当前树的右子树,右子树在后序遍历中的最后一位就是root-1,也就是前一位

通过观察我们可以发现,左子树在后序遍历中的最后一位与当前root之间相差了一个右子树的长度
那么我们可以由中序遍历的r-mid得到右子树的长度,所以左子树在后序遍历中的最后一位就是root-(r-mid)

感谢各位的愿意看到这里,我知道写的很烂,但是我已经在练习怎么写的更好了
在此献上我的main函数

int main()
{
    // freopen("in.txt", "r", stdin);
    int t;
    cin >> t;
    while (t--)
    {
        init();
        solve(0, n, n - 1);
        printf("%d\n", ans);
    }
    return 0;
}
  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值