返回目录
题目描述
给出一个二叉树如下图所示:

请由该二叉树生成一个新的二叉树,它满足其树中的每个节点将包含原始树中的左子树和右子树的和。

左子树表示该节点左侧叶子节点为根节点的一颗新树;右子树表示该节点右侧叶子节点为根节点的一颗新树。
输入描述
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 | 
| 说明 | 无 | 
题目解析
本题主要是考察二叉树的中序遍历,前序遍历,以及根据中序遍历和前序遍历还原二叉树结构。
二叉树的中序遍历即:左根右,即先遍历左子树,再遍历根,最后遍历右子树
二叉树的前序遍历即:根左右,即先遍历根,再遍历左子树,最后遍历右子树
二叉树的前序遍历序列中首元素就是根节点,比如题目描述中的前序遍历序列:

其中首元素6就是根节点。
知道根节点后,我们就可以去中序遍历序列中找打根值对应的节点,比如题目描述中的中序遍历序列:

上面中序遍历序列中有两个值为6的元素,那么他们都有可能为根,我们需要一一判断:
- 如果红色6(第一个6)是根,那么根据中序:“左根右” 的遍历特点,7 -2 就是左子树的中序遍历,6 9 就是右子树的中序遍历
- 如果绿色6(第二个6)是根,那么根据中序:“左根右” 的遍历特点,7 -2 6 就是左子树的中序遍历,9 就是右子树的中序遍历
上面两个情况中,我们根据中序遍历特点,得到了左子树的长度、右子树长度。
而一颗二叉树(子树)的序列长度是固定的,即一颗二叉树(子树)的中序遍历序列和前序遍历序列长度是相同的。
因此,我们通过中序遍历得到左子树、右子树长度,那么就可以在前序遍历中划分出左子树、右子树范围:
比如按照中序遍历序列中红色6(第一个6)作为根的话,那么左子树(7 -2)长度为2,右子树(6 9) 长度2,则前序遍历序列可进行如下划分:

此时,对比前序的左子树和中序的左子树是否节点相同,对比前序的右子树和中序的右子树是否相同

如果左右子树都一致,则当前根是正确根。
如果我们选错根,比如选中序遍历序列中第二个6作为根,则


可以发现中序、前序的左右子树是不一致的。
综上所述,即我们通过前序序列找到二叉树的根节点值(前序序列首元素),然后使用此根值,去中序序列中找根值位置,并划分出左子树长度,右子树长度,然后分别在前序、中序序列中,找出左子树序列、右子树序列,对比是否一致,如果一致,则中序序列中对应根值位置正确,否则错误。
根据前序、中序序列还原二叉树结构,也是按照上面逻辑,具体实现请看代码中buildTree函数,已添加详细注释。
另外,本题需要根据原始树(即根据中序、前序还原出来的树),改造出一个新树,新树的每个节点的值 = 其左右子树的所有节点值之和。
这里,我们可以在定义二叉树节点TreeNode结构时,多定义一个属性childSum用于记录节点的左右子树节点值之和。
Python算法源码
class TreeNode:
    def __init__(self, val):
        self.val = val  # 当前节点的值
        self.sum = 0  # 当前节点的左子树+右子树的和
        self.left = None
        self.right = None
# 中序遍历序列
inorder = []
# 前序遍历序列
preorder = []
# 记录中序遍历序列中,序列元素值所在位置,本题中可能存在重复元素,因此某个序列元素值可能有多个位置
indexMap = {}
def main():
    global inorder, preorder, indexMap
    inorder = list(map(int, input().split()))
    preorder = list(map(int, input().split()))
    length = len(inorder)
    for i in range(length):
        num = inorder[i]
        if num not in indexMap:
            indexMap[num] = []
        indexMap[num].append(i)
    # 根据中序序列和前序序列还原树结构
    root = build_tree(0, length - 1, 0, length - 1)
    # 记录新的二叉树的中序遍历序列
    inorder_traversal(root)
def inorder_traversal(root):
    if root is None:
        return
    # 先遍历左子树
    inorder_traversal(root.left)
    # 再遍历根
    print(root.sum, end=" ")
    # 最后遍历右子树
    inorder_traversal(root.right)
def build_tree(in_start, in_end, pre_start, pre_end):
    # 某个节点(子树)对应一段子序列,如果对应子序列范围不存在,则子树也不存在
    if pre_start > pre_end:
        return None
    # 先根据前序遍历序列得到根节点,前序序列的首元素就是根节点
    root_val = preorder[pre_start]
    root = TreeNode(root_val)
    # 在中序遍历序列中,找到对应根值的位置,这个位置可能有多个,但是只有一个是正确的
    for idx in indexMap[root_val]:
        # 如果对应根值位置越界,则不是正确的
        if idx < in_start or idx > in_end:
            continue
        # 如果中序的左子树,和前序的左子树不同,则对应根值位置不正确
        left_length = idx - in_start
        if not_equal(in_start, pre_start + 1, left_length):
            continue
        # 如果中序的右子树,和前序的右子树不同,则对应根值位置不正确
        right_length = in_end - idx
        if not_equal(idx + 1, pre_end - right_length + 1, right_length):
            continue
        # 找到正确根值位置后,开始分治递归处理左子树和右子树
        root.left = build_tree(in_start, idx - 1, pre_start + 1, pre_start + left_length)
        root.right = build_tree(idx + 1, in_end, pre_end - right_length + 1, pre_end)
        # 记录该节点:左子树+右子树的和(本题新二叉树节点的值)
        root.sum = (0 if root.left is None else (root.left.val + root.left.sum)) + \
                   (0 if root.right is None else (root.right.val + root.right.sum))
        break
    return root
def not_equal(start1, start2, size):
    arr1 = sorted(inorder[start1:start1 + size])
    arr2 = sorted(preorder[start2:start2 + size])
    for i in range(size):
        if arr1[i] != arr2[i]:
            return True
    return False
if __name__ == "__main__":
    main()C算法源码
#include <stdio.h>
#include <stdlib.h>
struct TreeNode {
    int num;
    int childSum;
    struct TreeNode* leftChild;
    struct TreeNode* rightChild;
};
struct TreeNode* createTreeNode(int num) {
    struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    node->num = num;
    node->childSum = 0;
    node->leftChild = NULL;
    node->rightChild = NULL;
    return node;
}
int midOrder[1000]; // Assuming a maximum size for midOrder and preOrder
int preOrder[1000];
int midIndexMap[1001][100]; // Assuming a maximum size for midIndexMap
int n;
int notEquals(int midL, int preL, int size) {
    int arr1[100], arr2[100]; // Assuming a maximum size for arr1 and arr2
    for (int i = 0; i < size; i++) {
        arr1[i] = midOrder[midL + i];
        arr2[i] = preOrder[preL + i];
    }
    for (int i = 0; i < size; i++) {
        if (arr1[i] != arr2[i])
            return 1;
    }
    return 0;
}
struct TreeNode* buildTree(int midL, int midR, int preL, int preR) {
    if (preL > preR)
        return NULL;
    int rootNum = preOrder[preL];
    struct TreeNode* root = createTreeNode(rootNum);
    for (int k = 0; k < 100; k++) {
        int idx = midIndexMap[rootNum][k];
        if (idx < midL || idx > midR)
            continue;
        int leftLen = idx - midL;
        if (notEquals(midL, preL + 1, leftLen))
            continue;
        int rightLen = midR - idx;
        if (notEquals(idx + 1, preR - rightLen + 1, rightLen))
            continue;
        root->leftChild = buildTree(midL, idx - 1, preL + 1, preL + leftLen);
        root->rightChild = buildTree(idx + 1, midR, preR - rightLen + 1, preR);
        int leftChildSum = (root->leftChild == NULL) ? 0 : (root->leftChild->num + root->leftChild->childSum);
        int rightChildSum = (root->rightChild == NULL) ? 0 : (root->rightChild->num + root->rightChild->childSum);
        root->childSum = leftChildSum + rightChildSum;
        break;
    }
    return root;
}
void getMidOrder(struct TreeNode* root, int* res, int* idx) {
    if (root == NULL)
        return;
    getMidOrder(root->leftChild, res, idx);
    res[(*idx)++] = root->childSum;
    getMidOrder(root->rightChild, res, idx);
}
char* getResult() {
    struct TreeNode* root = buildTree(0, n - 1, 0, n - 1);
    int res[1000]; // Assuming a maximum size for res
    int idx = 0;
    getMidOrder(root, res, &idx);
    char* result = (char*)malloc(10000 * sizeof(char)); // Assuming a maximum size for the result string
    int pos = 0;
    for (int i = 0; i < n; i++) {
        pos += sprintf(result + pos, "%d ", res[i]);
    }
    return result;
}
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &midOrder[i]);
    }
    for (int i = 0; i < n; i++) {
        scanf("%d", &preOrder[i]);
    }
    for (int j = 0; j < n; j++) {
        int num = midOrder[j];
        for (int k = 0; k < n; k++) {
            if (midIndexMap[num][k] == 0) {
                midIndexMap[num][k] = j;
                break;
            }
        }
    }
    printf("%s\n", getResult());
    return 0;
}Java算法源码
import java.util.*;
public class Main {
    static class TreeNode {
        int val; // 当前节点的值
        int sum; // 当前节点的左子树+右子树的和
        TreeNode left;
        TreeNode right;
        public TreeNode(int val) {
            this.val = val;
            this.sum = 0;
            this.left = null;
            this.right = null;
        }
    }
    // 中序遍历序列
    static int[] inorder;
    // 前序遍历序列
    static int[] preorder;
    // 记录中序遍历序列中,序列元素值所在位置,本题中可能存在重复元素,因此某个序列元素值可能有多个位置
    static HashMap<Integer, ArrayList<Integer>> indexMap = new HashMap<>();
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        inorder = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        preorder = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        int length = inorder.length;
        for (int i = 0; i < length; i++) {
            int num = inorder[i];
            indexMap.putIfAbsent(num, new ArrayList<>());
            indexMap.get(num).add(i);
        }
        // 根据中序序列和前序序列还原树结构
        TreeNode root = buildTree(0, length - 1, 0, length - 1);
        // 记录新的二叉树的的中序遍历序列
        StringJoiner sj = new StringJoiner(" ");
        inorderTraversal(root, sj);
        System.out.println(sj);
    }
    // 二叉树中序遍历
    public static void inorderTraversal(TreeNode root, StringJoiner sj) {
        if (root == null) {
            return;
        }
        // 先遍历左子树
        TreeNode left = root.left;
        if (left != null) {
            inorderTraversal(left, sj);
        }
        // 再遍历根
        sj.add(root.sum + "");
        // 最后遍历右子树
        TreeNode right = root.right;
        if (right != null) {
            inorderTraversal(right, sj);
        }
    }
    /**
     * 根据中序遍历序列、前序遍历序列还原树结构
     *
     * @param inStart  中序遍历子序列的左边界
     * @param inEnd    中序遍历子序列的右边界
     * @param preStart 前序遍历子序列的左边界
     * @param preEnd   前序遍历子序列的右边界
     * @return 树结构的根节点
     */
    public static TreeNode buildTree(int inStart, int inEnd, int preStart, int preEnd) {
        // 某个节点(子树)对应一段子序列,如果对应子序列范围不存在,则子树也不存在
        if (preStart > preEnd) return null;
        // 先根据前序遍历序列得到根节点,前序序列的首元素就是根节点
        int rootVal = preorder[preStart];
        TreeNode root = new TreeNode(rootVal);
        // 在中序遍历序列中,找到对应根值的位置,这个位置可能有多个,但是只有一个是正确的
        for (int idx : indexMap.get(rootVal)) {
            // 如果对应根值位置越界,则不是正确的
            if (idx < inStart || idx > inEnd) continue;
            // 如果中序的左子树,和前序的左子树不同,则对应根值位置不正确
            int leftLength = idx - inStart;
            if (notEqual(inStart, preStart + 1, leftLength)) continue;
            // 如果中序的右子树,和前序的右子树不同,则对应根值位置不正确
            int rightLength = inEnd - idx;
            if (notEqual(idx + 1, preEnd - rightLength + 1, rightLength)) continue;
            // 找到正确根值位置后,开始分治递归处理左子树和右子树
            root.left = buildTree(inStart, idx - 1, preStart + 1, preStart + leftLength);
            root.right = buildTree(idx + 1, inEnd, preEnd - rightLength + 1, preEnd);
            // 记录该节点:左子树+右子树的和(本题新二叉树节点的值)
            root.sum = (root.left == null ? 0 : (root.left.val + root.left.sum))
                    + (root.right == null ? 0 : (root.right.val + root.right.sum));
            break;
        }
        return root;
    }
    /**
     * 判断两个子数组是否相同(元素相同,顺序可以不同)
     *
     * @param start1 子数组1的左边界
     * @param start2 子数组2的左边界
     * @param size   子数组的长度
     * @return 子数组1和子数组2是否相同
     */
    public static boolean notEqual(int start1, int start2, int size) {
        int[] arr1 = Arrays.stream(Arrays.copyOfRange(inorder, start1, start1 + size)).sorted().toArray();
        int[] arr2 = Arrays.stream(Arrays.copyOfRange(preorder, start2, start2 + size)).sorted().toArray();
        for (int i = 0; i < size; i++) {
            if (arr1[i] != arr2[i]) {
                return true;
            }
        }
        return false;
    }
}
4 条评论
[...]2024 C卷 100分序号题目知 识 点难易程度1密码输入检测数据结构/栈☆☆☆2分配土地几何问题☆☆☆3找座位逻辑分析☆☆☆4智能成绩表动态条件分析☆☆☆5内存冷热标记多条件排序☆☆☆6螺旋数字矩阵逻辑分析☆☆☆7围棋的气逻辑分析☆☆☆8分割平衡字符串逻辑分析☆☆☆9机器人搬砖二分法☆☆☆10转盘寿司数据结构/栈/单调栈☆☆☆11小明找位置二分法☆☆☆12提取字符串的最长合法简单数学表达式双指[...]
[...]2024 C卷 100分序号题目知 识 点难易程度1密码输入检测数据结构/栈☆☆☆2分配土地几何问题☆☆☆3找座位逻辑分析☆☆☆4智能成绩表动态条件分析☆☆☆5内存冷热标记多条件排序☆☆☆6螺旋数字矩阵逻辑分析☆☆☆7围棋的气逻辑分析☆☆☆8分割平衡字符串逻辑分析☆☆☆9机器人搬砖二分法☆☆☆10转盘寿司数据结构/栈/单调栈☆☆☆11小明找位置二分法☆☆☆12提取字符串的最长合法简单数学表达式双指[...]
[...]2024 C卷 100分序号题目知 识 点难易程度1密码输入检测数据结构/栈☆☆☆2分配土地几何问题☆☆☆3找座位逻辑分析☆☆☆4智能成绩表动态条件分析☆☆☆5内存冷热标记多条件排序☆☆☆6螺旋数字矩阵逻辑分析☆☆☆7围棋的气逻辑分析☆☆☆8分割平衡字符串逻辑分析☆☆☆9机器人搬砖二分法☆☆☆10转盘寿司数据结构/栈/单调栈☆☆☆11小明找位置二分法☆☆☆12提取字符串的最长合法简单数学表达式双指[...]
[...]序号题目考点难易程度1二叉树计算二叉树前序、中序遍历☆☆☆25G网络建设最小生成树☆☆☆☆3找数字逻辑分析☆☆☆4符号运算数据结构 / 栈☆☆☆5爱吃蟠桃的孙悟空二分法☆☆☆6结队编程暴力枚举 二叉树索树☆☆☆7石头剪刀布游戏逻辑分析☆☆☆8攀登者2逻辑分析☆☆☆9分月饼递归☆☆☆10电脑病毒感染图论 / 单源最短路径(dijkstra☆☆☆[...]