华为OD机试C卷(200分)-二叉树计算

题目描述

给出一个二叉树如下图所示:
在这里插入图片描述
请由该二叉树生成一个新的二叉树,它满足其树中的每个节点将包含原始树中的左子树和右子树的和。
在这里插入图片描述
左子树表示该节点左侧叶子节点为根节点的一颗新树;右子树表示该节点右侧叶子节点为根节点的一颗新树。

输入描述

2行整数,第1行表示二叉树的中序遍历,第2行表示二叉树的前序遍历,以空格分割
例如:
7 -2 6 6 9
6 7 -2 9 6

输出描述

1行整数,表示求和树的中序遍历,以空格分割
例如:
-2 0 20 0 6
用例
输入 -3 12 6 8 9 -10 -7
8 12 -3 6 -10 9 -7
输出 0 3 0 7 0 2 0
说明 无

题目解析

二叉树的前序遍历首节点为根节点,在中序遍历中找到根节点所在位置。根节点左侧为左子树序列,右侧为右子树序列。通过比较左右子树序列长度以及元素是否相等,来判断所找到的根节点是否正确。

在这里插入代码片#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 10000

typedef struct TreeNode {
    int num; // 当前节点的值
    int childSum; // 当前节点的左子树+右子树的和
    struct TreeNode *leftChild;
    struct TreeNode *rightChild;
} TreeNode;

TreeNode *new_TreeNode(int num) {
    TreeNode *node = (TreeNode *) malloc(sizeof(TreeNode));
    node->num = num;
    node->childSum = 0;
    node->leftChild = NULL;
    node->rightChild = NULL;
    return node;
}

// 中序遍历序列
int midOrder[MAX_SIZE];

// 前序遍历序列
int preOrder[MAX_SIZE];

int cmp(const void *a, const void *b) {
    return *((int *) a) - *((int *) b);
}

/**
 * 判断两个子数组是否相同(元素相同,顺序可以不同)
 * @param midL 子数组1的左边界
 * @param preL 子数组2的左边界
 * @param size 子数组的长度
 * @return 子数组1和子数组2是否相同
 */
int notEquals(int midL, int preL, int size) {
    int arr1[size];
    int arr2[size];
    for (int i = 0; i < size; i++) {
        arr1[i] = midOrder[midL + i];
        arr2[i] = preOrder[preL + i];
    }

    qsort(arr1, size, sizeof(int), cmp);
    qsort(arr2, size, sizeof(int), cmp);

    for (int i = 0; i < size; i++) {
        if (arr1[i] != arr2[i]) {
            return 1;
        }
    }

    return 0;
}

/**
 * 根据中序遍历序列、前序遍历序列还原树结构
 * @param midL 中序遍历子序列的左边界
 * @param midR 中序遍历子序列的右边界
 * @param preL 前序遍历子序列的左边界
 * @param preR 前序遍历子序列的右边界
 * @return 树结构的根节点
 */
TreeNode *buildTree(int midL, int midR, int preL, int preR) {
    // 某个节点(子树)对应一段子序列,如果对应子序列范围不存在,则子树也不存在
    if (preL > preR) return NULL;

    // 先根据前序遍历序列得到根节点,前序序列的首元素就是根节点
    int rootNum = preOrder[preL];
    TreeNode *root = new_TreeNode(rootNum);

    // 在中序遍历序列中,找到对应根值的位置,这个位置可能有多个,但是只有一个是正确的
    for (int i = midL; i <= midR; i++) {
        if (midOrder[i] != rootNum) continue;

        // 如果中序的左子树,和前序的左子树不同,则对应根值位置不正确
        int leftLen = i - midL;
        if (notEquals(midL, preL + 1, leftLen)) continue;

        // 如果中序的右子树,和前序的右子树不同,则对应根值位置不正确
        int rightLen = midR - i;
        if (notEquals(i + 1, preR - rightLen + 1, rightLen)) continue;

        // 找到正确根值位置后,开始分治递归处理左子树和右子树
        root->leftChild = buildTree(midL, i - 1, preL + 1, preL + leftLen);
        root->rightChild = buildTree(i + 1, midR, preR - rightLen + 1, preR);

        // 记录该节点:左子树+右子树的和(本题新二叉树节点的值)
        root->childSum = (root->leftChild == NULL ? 0 : (root->leftChild->num + root->leftChild->childSum)) +
                         (root->rightChild == NULL ? 0 : (root->rightChild->num + root->rightChild->childSum));

        break;
    }

    return root;
}

// 二叉树中序遍历
void getMidOrder(TreeNode* root) {
    if (root == NULL) return;

    // 先遍历左子树
    TreeNode* leftChild = root->leftChild;
    if(leftChild != NULL) {
        getMidOrder(leftChild);
    }

    // 再遍历根
    printf("%d ", root->childSum);

    // 最后遍历右子树
    TreeNode* rightChild = root->rightChild;
    if(rightChild != NULL) {
        getMidOrder(rightChild);
    }
}

int main() {
    int size = 0;

    while (scanf("%d", &midOrder[size++])) {
        if (getchar() != ' ') break;
    }

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

    // 根据中序序列和前序序列还原树结构
    TreeNode *root = buildTree(0, size - 1, 0, size - 1);

    // 打印新的二叉树的的中序遍历序列
    getMidOrder(root);

    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我不会起名字呀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值