根据先序遍历和中序遍历生成后序遍历

本文详细介绍了如何根据先序和中序遍历的结果来生成后序遍历。首先,通过分析先序和后序遍历的特性,确定后序遍历的最后一个节点是先序遍历的头节点。接着,利用中序遍历找到头节点在中序遍历中的位置,以此划分左右子树,并递归地处理左右子树。文章还提出了使用哈希映射预先存储中序遍历中每个值的位置,以提高查找效率。最后给出了完整的Java代码实现。
摘要由CSDN通过智能技术生成

根据先序遍历和中序遍历生成后序遍历

作者:Grey

原文地址:

博客园:根据先序遍历和中序遍历生成后序遍历

CSDN:根据先序遍历和中序遍历生成后序遍历

问题描述

牛客:通过先序和中序数组生成后序数组

思路

假设有一棵二叉树

image

先序遍历的结果是

image

中序遍历的结果是

image

由于先序遍历大的调度逻辑是,先头,再左,再右

后序遍历的调度逻辑是:先左,再右,再头。

所以:后序遍历的最后一个节点,一定是先序遍历的头节点

定义递归函数

// 先序遍历数组pre的[l1...r1]区间
// 中序遍历数组in的[l2...r2]区间
// 生成后序遍历数组pos的[l3...r3]区间
void func(int[] pre, int l1, int r1, int[] in, int l2, int r2, int[] pos, int l3, r3)

依据以上推断,可以得到如下结论

// 后序遍历的最后一个节点,一定是先序遍历的头节点
pos[r3] = pre[l1];

然后,在中序数组中,我们可以定位到这个头节点的位置,即下图中标黄的位置,假设这个位置是index

image

这个index将中序数组分成了左右两个部分,由于中序遍历的调度过程是:先左,再头,再右,所以在中序遍历中[l2......index]区间内,是以index位置为头的左树中序遍历结果,[l2......index]区间内元素个数假设为b,那么在先序遍历中,从头往后数b个元素,即:[l1......l1+b]构成了以index位置为头的左树的先序遍历结果。

public static void func(int[] pre, int l1, int r1, int[] in, int l2, int r2, int[] pos, int l3, int r3) {
    if (l1 > r1) {
        // 避免了无效情况
        return;
    }
    if (l1 == r1) {
        // 只有一个数的时候
        pos[l3] = pre[l1];
    } else {
        // 不止一个数的时候
        pos[r3] = pre[l1];
        // index表示某个头在中序数组中的位置
        int index;
        // 可以优化
        for (index = l2; index <= r2; index++) {
            if (in[index] == pre[l1]) {
                break;
            }
        }
        int b = index - l2;
        func(pre, l1 + 1, l1 + b, in, l2, index - 1, pos, l3, l3 + b - 1);
        func(pre, l1 + b + 1, r1, in, index + 1, r2, pos, l3 + b, r3 - 1);
    }
}

优化

在递归函数func中,有一个遍历的行为,

for (index = l2; index <= r2; index++) {
    if (in[index] == pre[l1]) {
        break;
    }
}

如果每次递归都要遍历一下,那么效率会降低,所以可以在一开始就设置一个map,存一下中序遍历中每个值所在的位置信息,这样就不需要通过遍历来找位置了,方法如下:

Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
    inOrder[i] = in.nextInt();
    map.put(inOrder[i], i);
}

这样预处理以后,每次index的位置不需要遍历得到,只需要

            int index = map.get(pre[l1]);

即可,完整代码见

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] preOrder = new int[n];
        int[] inOrder = new int[n];
        for (int i = 0; i < n; i++) {
            preOrder[i] = in.nextInt();
        }
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            inOrder[i] = in.nextInt();
            map.put(inOrder[i], i);
        }
        int[] posOrder = new int[n];
        func(preOrder, 0, n - 1, inOrder, 0, n - 1, posOrder, 0, n - 1, map);
        for (int i = 0; i < n; i++) {
            System.out.print(posOrder[i] + " ");
        }
        in.close();
    }

    public static void func(int[] pre, int l1, int r1, int[] in, int l2, int r2, int[] pos, int l3, int r3, Map<Integer, Integer> map) {
        if (l1 > r1) {
            // 避免了无效情况
            return;
        }
        if (l1 == r1) {
            // 只有一个数的时候
            pos[l3] = pre[l1];
        } else {
            // 不止一个数的时候
            pos[r3] = pre[l1];
            // index表示某个头在中序数组中的位置
            int index = map.get(pre[l1]);
            int b = index - l2;
            func(pre, l1 + 1, l1 + b, in, l2, index - 1, pos, l3, l3 + b - 1, map);
            func(pre, l1 + b + 1, r1, in, index + 1, r2, pos, l3 + b, r3 - 1, map);
        }
    }

}

更多

算法和数据结构学习笔记

算法和数据结构学习代码

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,这是一个关于数据结构的问题,让我来为您解答。首先,我们需要定义一个二叉树的结构体: ```c typedef struct BiNode { char data; // 节点中存储的数据 struct BiNode *lchild, *rchild; // 左子节点和右子节点 } BiNode, *BiTree; ``` 然后,我们可以编写一个函数生成一个二叉树: ```c void createBiTree(BiTree *T) { char c; scanf("%c", &c); // 输入一个节点的数据 if (c == '#') { // 如果是 #,则表示该节点为空 *T = NULL; } else { *T = (BiTree)malloc(sizeof(BiNode)); // 为节点分配内存 (*T)->data = c; // 存储节点数据 createBiTree(&(*T)->lchild); // 创建左子节点 createBiTree(&(*T)->rchild); // 创建右子节点 } } ``` 接下来,我们可以编写函数分别输出二叉树先序遍历中序遍历后序遍历: ```c void preOrderTraverse(BiTree T) { if (T == NULL) { return; } printf("%c ", T->data); // 输出节点数据 preOrderTraverse(T->lchild); // 遍历左子树 preOrderTraverse(T->rchild); // 遍历右子树 } void inOrderTraverse(BiTree T) { if (T == NULL) { return; } inOrderTraverse(T->lchild); // 遍历左子树 printf("%c ", T->data); // 输出节点数据 inOrderTraverse(T->rchild); // 遍历右子树 } void postOrderTraverse(BiTree T) { if (T == NULL) { return; } postOrderTraverse(T->lchild); // 遍历左子树 postOrderTraverse(T->rchild); // 遍历右子树 printf("%c ", T->data); // 输出节点数据 } ``` 最后,我们可以在主函数中调用这些函数: ```c int main() { BiTree T; printf("请输入二叉树的节点信息(以 # 表示空节点):\n"); createBiTree(&T); printf("先序遍历结果为:"); preOrderTraverse(T); printf("\n"); printf("中序遍历结果为:"); inOrderTraverse(T); printf("\n"); printf("后序遍历结果为:"); postOrderTraverse(T); printf("\n"); return 0; } ``` 这样,就可以实现用 C 建立二叉树,并通过调用函数输出先序遍历中序遍历后序遍历的结果了。希望这个答案能够帮到您。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GreyZeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值