OD_2024_C卷_200分_4、二叉树计算【JAVA】【二叉树前序、中序遍历】

news/2024/5/19 10:46:50 标签: java, 算法, OD

在这里插入图片描述
在这里插入图片描述

代码

java">package odjava;

import java.util.*;

public class 四_二叉树计算 {
    static class TreeNode {
        int num; // 当前节点的值
        int childSum; // 当前节点的左子树+右子树的和
        TreeNode leftChild;
        TreeNode rightChild;

        public TreeNode(int num) {
            this.num = num;
            this.childSum = 0;
            this.leftChild = null;
            this.rightChild = null;
        }
    }

    // 中序遍历序列
    static int[] midOrder;
    // 前序遍历序列
    static int[] preOrder;
    // 记录中序遍历序列中,序列元素值所在位置,本题中可能存在重复元素,因此某个序列元素值可能有多个位置
    static HashMap<Integer, ArrayList<Integer>> midIndexMap = new HashMap<>();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        midOrder = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        preOrder = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();

        int n = midOrder.length;
        for (int i = 0; i < n; i++) {
            int num = midOrder[i];
            midIndexMap.putIfAbsent(num, new ArrayList<>());
            midIndexMap.get(num).add(i);
        }

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

        // 记录新的二叉树的的中序遍历序列
        StringJoiner sj = new StringJoiner(" ");
        getMidOrder(root, sj);
        System.out.println(sj);
    }

    // 二叉树中序遍历
    public static void getMidOrder(TreeNode root, StringJoiner sj) {
        if (root == null) {
            return;
        }
        // 先遍历左子树
        TreeNode leftChild = root.leftChild;
        if (leftChild != null) {
            getMidOrder(leftChild, sj);
        }
        // 再遍历根
        sj.add(root.childSum + "");
        // 最后遍历右子树
        TreeNode rightChild = root.rightChild;
        if (rightChild != null) {
            getMidOrder(rightChild, sj);
        }
    }

    /**
     * 根据中序遍历序列、前序遍历序列还原树结构
     *
     * @param midL 中序遍历子序列的左边界
     * @param midR 中序遍历子序列的右边界
     * @param preL 前序遍历子序列的左边界
     * @param preR 前序遍历子序列的右边界
     * @return 树结构的根节点
     */
    public static 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 idx : midIndexMap.get(rootNum)) {
            // 如果对应根值位置越界,则不是正确的
            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);

            // 记录该节点:左子树+右子树的和(本题新二叉树节点的值)
            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;
    }

    /**
     * 判断两个子数组是否相同(元素相同,顺序可以不同)
     *
     * @param midL 子数组1的左边界
     * @param preL 子数组2的左边界
     * @param size 子数组的长度
     * @return 子数组1和子数组2是否相同
     */
    public static boolean notEquals(int midL, int preL, int size) {
        int[] arr1 = Arrays.stream(Arrays.copyOfRange(midOrder, midL, midL + size)).sorted().toArray();
        int[] arr2 = Arrays.stream(Arrays.copyOfRange(preOrder, preL, preL + size)).sorted().toArray();

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

        return false;
    }
}


http://www.niftyadmin.cn/n/5420832.html

相关文章

Java对接(BSC)币安链 | BNB与BEP20的开发实践(三)水龙头 WEB3

上一节我们用代码来实现BNB转账、BEP20转账、链上交易监控 这一节我们讲一个币安测试链如何获取到BNB、USDT等BEP20数字货币&#xff08;水龙头&#xff09;来让我们前期测试开发。 首先我们先来创建一个地址&#xff1a; /*** 创建地址(离线)*/Overridepublic Map<Strin…

阿里巴巴国际站爬虫工具 商家电话采集软件教程

阿里巴巴国际站爬虫工具是一种用于采集阿里巴巴国际站上商家电话的软件。这种软件的使用可以方便用户快速获取到商家的联系电话&#xff0c;有助于商业合作、市场调研等用途。以下是一份简单的教程&#xff0c;帮助你了解如何使用阿里巴巴国际站爬虫工具。 第一步&#xff1a;…

excel批量数据导入时用poi将数据转化成指定实体工具类

1.实现目标 excel进行批量数据导入时&#xff0c;将批量数据转化成指定的实体集合用于数据操作&#xff0c;实现思路&#xff1a;使用注解将属性与表格中的标题进行同名绑定来赋值。 2.代码实现 2.1 目录截图如下 2.2 代码实现 package poi.constants;/*** description: 用…

使用 ReclaiMe Pro 恢复任意文件系统(Win/Linux/MacOS)

天津鸿萌科贸发展有限公司是 ReclaiMe Pro 数据恢复软件授权代理商。 ReclaiMe Pro 是一个通用工具包&#xff0c;几乎可以用于从所有文件系统&#xff08;从 Windows 系列文件系统、Linux 和 MacOS&#xff09;中恢复数据。此外&#xff0c;考虑到数据恢复工作的具体情况&…

AD然后批量更改PCB中标号(位号)大小

1、选择一个位号&#xff0c;右键&#xff0c;查找相似对象 2、修改“Designator”为“same”&#xff0c;点击“应用”&#xff0c;“确定” 3、修改字体高度和宽度

2403d,d解析c++符号

原文 这里有个简单的无需更改动态库或应该动态链接到它的DMD项目中源码的方法.当然,并不能解决潜在的C调用约定问题(C不存在),但可在有它们时再调查. 我做了个小小的概念证明,它有效.为了具体起见,假设动态库是libx.dll,使用安装了最新MSYS2的(mingw64)gcc构建它,因为这是我需…

Qt+FFmpeg+opengl从零制作视频播放器-2.环境搭建

1.环境介绍 Qt5.9.0VS2017ffmpeg4.4.3&#xff0c;这里版本均使用64位版本。 Qt的版本大于我这个版本都行。 opengl3.3&#xff0c;Qt已经封装好了QOpenGLWidget&#xff0c;直接使用Qt的就行。 Qt版本下载&#xff1a;Index of /archive/qt 2.ffmpeg下载 Releases BtbN…

哪些泛域名ssl证书一年送一月

泛域名SSL数字证书&#xff0c;也称之为通配符SSL证书&#xff0c;是数字证书中比较特殊的产品。与传统的单域名SSL证书不同&#xff0c;泛域名SSL证书能够同时保护多个域名站点&#xff0c;只对域名站点的类型有限制——只能保护主域名以及主域名下的所有子域名站点。今天就随…