题目描述
给出一个二叉树如下图所示:
请由该二叉树生成一个新的二叉树,它满足其树中的每个节点将包含原始树中的左子树和右子树的和。
左子树表示该节点左侧叶子节点为根节点的一颗新树;右子树表示该节点右侧叶子节点为根节点的一颗新树。
输入描述
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;
}