史上完美解析——已知中序后序遍历,求二叉树先序遍历

目录

前言

题解代码

核心点解析

验证

总结



前言

数据结构,树,递归是其核心;


题解代码

对于该题的解答,给出python代码如下(后续补充Java和C):

文件名为:for_preorder.py

res = []


def preorder_from_in_and_post(inorder, postorder):
    if len(inorder) == len(postorder) == 1:
        res.append(inorder[0])
    if len(inorder) == len(postorder) == 2:
        res.append(postorder[-1])
        res.append(postorder[0])
    if len(inorder) == len(postorder) >= 3:
        head = postorder[-1]
        idx = inorder.index(head)
        res.append(head)
        preorder_from_in_and_post(inorder[0:idx], postorder[0:idx])
        preorder_from_in_and_post(inorder[idx + 1:], postorder[idx:-1])


# preorder_from_in_and_post(
#     [200, 323, 990, 463, 154, 673, 927, 703, 873, 174, 473, 997, 983, 460, 85, 513, 56, 416, 981, 563],
#     [200, 323, 463, 154, 990, 673, 703, 174, 873, 983, 997, 473, 85, 513, 416, 563, 981, 56, 460, 927])
# print('{} {}'.format('先序:', res))
# 中序:200, 323, 990, 463, 154, 673, 927, 703, 873, 174, 473, 997, 983, 460, 85, 513, 56, 416, 981, 563
# 后序:200, 323, 463, 154, 990, 673, 703, 174, 873, 983, 997, 473, 85, 513, 416, 563, 981, 56, 460, 927
# 先序:927, 673, 990, 323, 200, 154, 463, 460, 473, 873, 703, 174, 997, 983, 56, 513, 85, 981, 416, 563

核心点解析

1.后序遍历的最后一个数字,是整棵树的头节点head;
2.以head切割中序遍历,head左边序列必定为左子树,head右边序列必定为右子树;
3.先序遍历顺序:头 -> 左 -> 右;
4.递归含义/函数含义:找出当前序列的头节点;
5.将目标序列(中序,后序)传进递归函数,目标序列将被分成2个子序列,即左子树和右子树
    此时,将左子树先传进递归函数,再将右子树传进递归函数;
    变成了,求2个子树序列的头节点;
    大规模问题,借由递归,变成一个个子问题,子问题的解决变得越来越简单;
6.由于左子树先进入递归函数,右子树后进入递归函数,并且递归前,已经获取当前头节点;
    所以天然符合,头 -> 左 -> 右的顺序;即先序;

    此时,无论是直接打印,还是先存入列表,最后都可以得到目标序列的先序遍历;

验证

接下来是验证:

1.对数器验证:
    此处使用的对数器逻辑是:用代码构造二叉树(构造的树可能是完全二叉树,也可能不是),利用正常的递归逻辑获取中序/后序/先序遍历;
    将上一个步骤的中序/后序遍历传入解答代码中,得出先序遍历,并和上一步骤的先序遍历,进行对比;

2.二叉树构造代码(文件名为:create_tree.py):

import random


class TreeNode:
    def __init__(self):
        self.data = None
        self.left = None
        self.right = None
        
# 树生成伪代码:
# init head
# insert head into stack: target
# candidates = new stack: target
# while length(node_data) > 0:
# 	while stack: target.length > 0:
# 		node = stack: target.pop()
# 		node.data = node_data.pop()
# 		node.left = new TreeNode
# 		node.right = new TreeNode
# 		insert node.left into candidates
# 		insert node.right into candidates
# 	stack: target = candidates	#随机取candidates中的元素
# 	candidates = new stack: target


# 构造对数器

def createTree(head):
    node_data = random.sample(list(range(1, 1001)), 20)
    target = [head]
    candidates = []
    while len(node_data) > 0:
        while len(target) > 0:
            node = target.pop()
            node.data = node_data.pop()
            if len(node_data) == 0:
                break
            node.left = TreeNode()
            node.right = TreeNode()
            candidates.append(node.left)
            candidates.append(node.right)
        target = random.sample(candidates, round(random.uniform(0.75, 1) * len(candidates)))
        candidates.clear()


inorder = []
postorder = []
preorder = []


def get_inorder(head):
    if head and head.left:
        get_inorder(head.left)
    if head and head.data:
        inorder.append(head.data)
    if head and head.right:
        get_inorder(head.right)


def get_postorder(head):
    if head and head.left:
        get_postorder(head.left)
    if head and head.right:
        get_postorder(head.right)
    if head and head.data:
        postorder.append(head.data)


def get_preorder(head):
    if head and head.data:
        preorder.append(head.data)
    if head and head.left:
        get_preorder(head.left)
    if head and head.right:
        get_preorder(head.right)


# init_head = TreeNode()
# createTree(init_head)
# get_inorder(init_head)
# get_postorder(init_head)
# get_preorder(init_head)
# print('{} {}'.format('中序:', inorder))
# print('{} {}'.format('后序:', postorder))
# print('{} {}'.format('先序:', preorder))

3.测试(文件名为main_test.py):

        进行10万次测试,发现每一次对比结果,都是一致的,确认算法正确性;

from create_tree import *
from for_preorder import *
flag = True
for i in range(100000):
    init_h = TreeNode()
    createTree(init_h)
    get_inorder(init_h)
    get_postorder(init_h)
    get_preorder(init_h)
    preorder_from_in_and_post(inorder, postorder)
    if res != preorder:
        print("false!!!")
        flag = False
    res.clear()
    inorder.clear()
    postorder.clear()
    preorder.clear()
if flag:
    print("finish!!!")
    

执行结果如下:


总结

构造对数器,进行验证;理解树的递归,活用树的递归。

在C语言中,如果你已经知道了二叉树序遍历(Preorder Traversal)和中序遍历(Inorder Traversal),可以通过这两个列重构出后序遍历(Postorder Traversal)。这是因为对于任何一个节点,它的后序遍历通常是左子树 -> 右子树 -> 节点本身。 首,我们需要三个指针分别表示当前节点、前驱节点和待访问的左子树。后序遍历算法步骤如下: 1. 初始化后序遍历结果数组 `postorder` 和长度 `size`。 2. 使用序遍历构建线索(即通过第一个元素找到根节点): - 序遍历的第一个元素作为根节点的线索。 3. 对于线索所指的节点,递归地处理左子树和右子树: - 如果线索指向的是左子节点,那么它就是后序遍历中的下一个节点,将其添加到 `postorder` 并更新线索到下一个元素。 - 如果线索指向的是右子节点,那么需要完成左子树的后序遍历,然后继续这个右子节点。 4. 当线索为空时,说明已经处理完了当前节点及其所有子节点,将当前节点添加到 `postorder` 的最后一位,并返回 `size`。 5. 最终 `postorder[0:size-1]` 就是后序遍历的结果。 以下是一个简单的C语言实现示例: ```c #include <stdio.h> typedef struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; } TreeNode; // 从序遍历和中序遍历构造后序遍历 void buildPostorder(int preorder[], int inorder[], TreeNode* root, int size, int *postorder, int postSize) { if (root == NULL || size <= 0) return; // 找到根节点在中序遍历中的位置 for (int i = 0; i < size && inorder[i] != root->val; i++) ; // 递归处理左右子树 buildPostorder(preorder, inorder, root->left, i, postorder, postSize); buildPostorder(preorder + i + 1, inorder + i + 1, root->right, size - (i + 1), postorder, postSize); // 将根节点添加到后序遍历的末尾 postorder[postSize++] = preorder[size - 1]; } int main() { // 示例输入:序遍历 [1, 2, 4, 5, 3, 6] // 中序遍历 [4, 2, 5, 1, 3, 6] int preorder[] = {1, 2, 4, 5, 3, 6}; int inorder[] = {4, 2, 5, 1, 3, 6}; int size = sizeof(preorder) / sizeof(preorder[0]); int postSize = 0; TreeNode* root = createTreeFromPreAndInOrder(preorder, inorder); // 根据给定的和中创建二叉树 int postorder[size]; // 后序遍历结果数组 buildPostorder(preorder, inorder, root, size, postorder, &postSize); // 输出后序遍历结果 for (int i = 0; i < postSize; i++) { printf("%d ", postorder[i]); } return 0; } ``` 注意,这里假设有一个 `createTreeFromPreAndInOrder` 函数用于根据给定的序遍历和中序遍历构建二叉树,你需要自定义这个函数。此外,上述代码未包含错误处理和边界条件检查,实际应用时请确保适当增加。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值