华为OD机试 - 创建二叉树(Java 2024 E卷 200分)
题目描述
给定一系列树状结构操作的问题,通过 Q 次查询还原树结构并输出结果。题目要求实现一个类 Solution,其方法 recoverTree 需要根据输入的操作数组 operations 还原树的结构,并返回树的根节点。每个操作 operations[i] = [height, index] 表示在高度为 height 的位置插入一个索引为 index 的节点。
-
树节点定义:
- 每个节点
node存在左子节点,则初始为null。 - 每个节点
node存在右子节点,则初始为null。 - 每个节点存储一个高度值
height和索引值index。
- 每个节点
-
输入要求:
height和index初始为 0,并按计数递增。index存储节点的创建顺序。
-
注意事项:
- 输入用例保证每次操作对应的节点已经存在。
- 控制台输出的内容是根据还原后的树根节点,按照层次遍历方式 Q 次查询打印的结果。
输入输出示例
示例 1:
输入: operations = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]
输出: [0, 0, 1, 1, 0]
解释:
[0, 0]:创建高度 0,索引 0 的节点,作为根节点。[0, 1]:创建高度 0,索引 1 的节点,插入到根节点的左子树(因为高度相同,按索引顺序)。[1, 1]:创建高度 1,索引 1 的节点,插入到根节点的右子树(高度更高)。[1, 0]:创建高度 1,索引 0 的节点,插入到根节点的左子树(高度更高)。[0, 0]:查询高度 0,索引 0 的节点,返回其值 0。
示例 2:
输入: operations = [[-1, 0], [1, 3], [null, 2]]
输出: [-1, 0, 1, 3, null, 2]
解释:
[-1, 0]:创建高度 -1,索引 0 的节点,作为根节点。[1, 3]:创建高度 1,索引 3 的节点,插入到根节点的右子树(高度更高)。[null, 2]:查询高度 null,索引 2 的节点,返回 null。
解题思路
问题分析
题目要求根据一系列操作还原一棵树,并通过查询返回指定节点的值。核心在于:
- 树的构建:根据
operations数组中的[height, index]创建节点并插入树中。 - 插入规则:高度决定节点的层级,索引决定插入顺序。
- 查询操作:根据
[height, index]找到对应节点并返回其值。
算法选择
- 树结构:使用二叉树结构存储节点,每个节点包含
height和index,并有左右子节点。 - 插入规则:
- 如果高度相同,优先插入到左子树。
- 如果高度更高,优先插入到右子树。
- 如果高度更低,插入到左子树。
- 层次遍历:在查询时,通过层次遍历找到目标节点。
步骤详解
- 定义节点类:包含
height、index、左子节点和右子节点。 - 构建树:
- 遍历
operations,对于每个操作:- 如果
height不为null,创建新节点并插入树中。 - 插入时,比较
height和index,决定插入到左子树还是右子树。
- 如果
- 遍历
- 查询节点:
- 遍历
operations,对于查询操作(height为null),通过层次遍历找到目标节点并返回其值。
- 遍历
- 返回结果:按照查询顺序返回结果数组。
代码实现
Java
class TreeNode {int height;int index;TreeNode left;TreeNode right;TreeNode(int height, int index) {this.height = height;this.index = index;this.left = null;this.right = null;}
}class Solution {public int[] recoverTree(int[][] operations) {TreeNode root = null;List<Integer> result = new ArrayList<>();// 构建树for (int[] op : operations) {int height = op[0];int index = op[1];// 如果 height 不为 null,插入节点if (height != Integer.MIN_VALUE) {TreeNode node = new TreeNode(height, index);if (root == null) {root = node;} else {insertNode(root, node);}}}// 查询节点for (int[] op : operations) {int height = op[0];int index = op[1];if (height == Integer.MIN_VALUE) {// 查询操作TreeNode target = findNode(root, index);if (target == null) {result.add(null);} else {result.add(target.height);}} else {result.add(height);}}// 转换为数组int[] res = new int[result.size()];for (int i = 0; i < result.size(); i++) {if (result.get(i) == null) {res[i] = Integer.MIN_VALUE; // 用 MIN_VALUE 表示 null} else {res[i] = result.get(i);}}return res;}private void insertNode(TreeNode root, TreeNode node) {TreeNode current = root;while (true) {if (node.height == current.height) {if (current.left == null) {current.left = node;break;} else {current = current.left;}} else if (node.height > current.height) {if (current.right == null) {current.right = node;break;} else {current = current.right;}} else {if (current.left == null) {current.left = node;break;} else {current = current.left;}}}}private TreeNode findNode(TreeNode root, int index) {if (root == null) return null;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {TreeNode node = queue.poll();if (node.index == index) return node;if (node.left != null) queue.offer(node.left);if (node.right != null) queue.offer(node.right);}return null;}
}
Python
class TreeNode:def __init__(self, height, index):self.height = heightself.index = indexself.left = Noneself.right = Noneclass Solution:def recoverTree(self, operations):root = Noneresult = []# 构建树for height, index in operations:if height is not None:node = TreeNode(height, index)if root is None:root = nodeelse:self.insert_node(root, node)# 查询节点for height, index in operations:if height is None:target = self.find_node(root, index)result.append(target.height if target else None)else:result.append(height)return resultdef insert_node(self, root, node):current = rootwhile True:if node.height == current.height:if current.left is None:current.left = nodebreakelse:current = current.leftelif node.height > current.height:if current.right is None:current.right = nodebreakelse:current = current.rightelse:if current.left is None:current.left = nodebreakelse:current = current.leftdef find_node(self, root, index):if not root:return Nonefrom collections import dequequeue = deque([root])while queue:node = queue.popleft()if node.index == index:return nodeif node.left:queue.append(node.left)if node.right:queue.append(node.right)return None
C++
#include <vector>
#include <queue>
using namespace std;class TreeNode {
public:int height;int index;TreeNode* left;TreeNode* right;TreeNode(int h, int idx) : height(h), index(idx), left(nullptr), right(nullptr) {}
};class Solution {
public:vector<int> recoverTree(vector<vector<int>>& operations) {TreeNode* root = nullptr;vector<int> result;// 构建树for (const auto& op : operations) {int height = op[0];int index = op[1];if (height != INT_MIN) {TreeNode* node = new TreeNode(height, index);if (!root) {root = node;} else {insertNode(root, node);}}}// 查询节点for (const auto& op : operations) {int height = op[0];int index = op[1];if (height == INT_MIN) {TreeNode* target = findNode(root, index);if (target) {result.push_back(target->height);} else {result.push_back(INT_MIN);}} else {result.push_back(height);}}return result;}private:void insertNode(TreeNode* root, TreeNode* node) {TreeNode* current = root;while (true) {if (node->height == current->height) {if (!current->left) {current->left = node;break;} else {current = current->left;}} else if (node->height > current->height) {if (!current->right) {current->right = node;break;} else {current = current->right;}} else {if (!current->left) {current->left = node;break;} else {current = current->left;}}}}TreeNode* findNode(TreeNode* root, int index) {if (!root) return nullptr;queue<TreeNode*> q;q.push(root);while (!q.empty()) {TreeNode* node = q.front();q.pop();if (node->index == index) return node;if (node->left) q.push(node->left);if (node->right) q.push(node->right);}return nullptr;}
};
JavaScript
class TreeNode {constructor(height, index) {this.height = height;this.index = index;this.left = null;this.right = null;}
}/*** @param {number[][]} operations* @return {number[]}*/
var recoverTree = function(operations) {let root = null;let result = [];// 构建树for (let [height, index] of operations) {if (height !== null) {let node = new TreeNode(height, index);if (!root) {root = node;} else {insertNode(root, node);}}}// 查询节点for (let [height, index] of operations) {if (height === null) {let target = findNode(root, index);result.push(target ? target.height : null);} else {result.push(height);}}return result;function insertNode(root, node) {let current = root;while (true) {if (node.height === current.height) {if (!current.left) {current.left = node;break;} else {current = current.left;}} else if (node.height > current.height) {if (!current.right) {current.right = node;break;} else {current = current.right;}} else {if (!current.left) {current.left = node;break;} else {current = current.left;}}}}function findNode(root, index) {if (!root) return null;let queue = [root];while (queue.length) {let node = queue.shift();if (node.index === index) return node;if (node.left) queue.push(node.left);if (node.right) queue.push(node.right);}return null;}
};
复杂度分析
- 时间复杂度:O(Q * H),其中 Q 是操作次数,H 是树的高度。每次插入和查询都需要遍历树的深度,平均情况下为 O(log Q),最坏情况下(树退化为链)为 O(Q)。
- 空间复杂度:O(Q)。树中最多有 Q 个节点,存储树和队列的空间为 O(Q)。
测试用例示例
测试用例 1:
输入: operations = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]
预期输出: [0, 0, 1, 1, 0]
解释: 按照操作构建树,最后查询高度 0,索引 0 的节点,返回 0。
测试用例 2:
输入: operations = [[-1, 0], [1, 3], [null, 2]]
预期输出: [-1, 0, 1, 3, null, 2]
解释: 构建树后,查询索引 2 的节点,未找到,返回 null。
测试用例 3:
输入: operations = [[0, 0], [1, 1], [null, 1]]
预期输出: [0, 0, 1, 1]
解释: 构建树后,查询索引 1 的节点,返回高度 1。
问题总结
本题是一个树结构的构建和查询问题,核心在于根据高度和索引的规则插入节点,并通过层次遍历查询目标节点。算法的关键点包括:
- 插入规则:高度决定插入方向,相同高度优先左子树。
- 查询效率:通过层次遍历查找目标节点。
- 边界处理:处理
null查询和未找到节点的情况。
该算法适用于动态构建树并查询的场景,但在树退化为链时效率较低。可能的优化方向包括使用平衡树(如 AVL 树或红黑树)来降低树高,从而将时间复杂度优化到 O(Q * log Q)。在实际应用中,例如文件系统或组织结构管理,可以用类似方法动态构建和查询树结构。
相关文章:
华为OD机试 - 创建二叉树(Java 2024 E卷 200分)
题目描述 给定一系列树状结构操作的问题,通过 Q 次查询还原树结构并输出结果。题目要求实现一个类 Solution,其方法 recoverTree 需要根据输入的操作数组 operations 还原树的结构,并返回树的根节点。每个操作 operations[i] [height, inde…...
c++基础知识-图论进阶
一、拓扑排序 1、基础知识 1)什么是拓扑排序 对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若,则u在线性序列中出现在v之前。 2)拓扑排序的操作方法 重复执行…...
[Java实战]Spring Boot服务CPU 100%问题排查:从定位到解决
Spring Boot服务CPU 100%问题排查:从定位到解决 1. 引言 当Spring Boot服务出现CPU占用率100%时,系统性能会急剧下降,甚至导致服务不可用。本文将通过真实代码案例,详细讲解如何快速定位问题根源,并提供解决方案。无…...
1.6 极限存在准则
1.夹逼定理(迫敛定理) 1.1 数列型 1.1.1 准则 1.2 函数型 2. 两个重要极限...
Fisher信息、梯度方差与学习率调度器的计算流程
Fisher信息、梯度方差与学习率调度器的计算流程 目录 Fisher信息、梯度方差与学习率调度器的计算流程**步骤1:定义模型与数据集****步骤2:计算梯度与Fisher信息****步骤3:计算梯度方差****步骤4:定义学习率调度器****步骤5:参数更新流程****示例输出****关键概念说明**步骤…...
Model Context Protocol 的生命周期
生命周期阶段 生命周期分为三个主要阶段: 初始化阶段 (Initialization) 客户端与服务器建立协议版本兼容性。交换并协商能力。分享实现细节。客户端必须发送 initialize 请求,包含支持的协议版本、客户端能力和客户端实现信息。服务器必须响应其自身能力…...
Go语言中的错误处理与异常恢复:性能对比与实践思考
Gone是一款轻量级Go依赖注入框架,通过简洁的标签声明实现自动组件管理。它提供零侵入设计、完整生命周期控制和极低运行时开销,让开发者专注于业务逻辑而非依赖关系处理。 项目地址: https://github.com/gone-io/gone 文章目录 Go的错误处理哲…...
CSS 属性选择器详解
CSS 属性选择器详解 引言 CSS(层叠样式表)是网页设计中的重要组成部分,它用于控制网页元素的样式和布局。属性选择器是CSS选择器的一种,它允许开发者根据元素的特定属性来选择和样式化元素。本文将详细讲解CSS属性选择器的概念、语法以及常用属性选择器的使用方法。 一、…...
大华SDK协议在智联视频超融合平台中的接入方法
一. 大华SDK协议详解 (一)、大华SDK协议概述 大华SDK(Software Development Kit)协议是大华股份为开发者提供的一套软件开发工具包,旨在帮助开发者快速集成大华设备(如摄像头、NVR、DVR等)的功…...
卓越的用户体验需要智能内容
摘要:这篇文章指出静态文档已无法满足现代用户的需求,而智能内容则是构建卓越用户体验的关键。文章从智能内容的定义、优势和实际应用等方面进行了详细阐述,并强调了企业应积极拥抱智能内容,以提升客户满意度、降低成本并创造新的…...
【蓝桥杯】1124修建公路1(Kruskal算法)
思路 找到能够连通所有城市的最小树即可,可用Prim或Kruscal。 !!注意,m的范围是包括0的,可就是包含没有道路的情况,要单独输出0 code import os import sys# 输入 n,m map(int,input().split()) road …...
传感云揭秘:边缘计算的革新力量
在当今快速发展的科技时代,传感云和边缘计算系统正逐渐成为人们关注的焦点。传感云作为物联网与云计算的结合体,通过虚拟化技术将物理节点转化为多个服务节点,为用户提供高效、便捷的服务。而边缘计算则是一种靠近数据源头或物端的网络边缘侧…...
Bigemap Pro 的三种地图下载方式
地图下载通常是是最基础但也最重要的任务之一,无论是进行空间分析、制作专题地图,还是进行数据可视化,高质量的地图数据都是不可或缺的。Bigemap Pro提供了三种地图下载方式,分别适用于不同的场景和需求。无论是免费版用户还是专业…...
Python直方图:从核密度估计到高维空间解析
一、直方图的核心原理与数学本质 数据分布的视觉解码器 直方图(Histogram)是数据科学家的"分布显微镜",通过将连续数据划分为等宽区间(Bin),统计各区间的频数/频率,用相邻矩形条直观…...
0基础 | 恒流源专题
目录 tip1:低端反馈编辑 tip2: 恒流源电路的设计注意事项 tip3:三极管输出恒定电流受运放输出电流控制 tip4:高端反馈 基本逻辑: 当负端Vref不输入电压时, 当负端Vref输入电压时 tip1:低端反馈 判…...
Cannl 数据同步-ES篇
Cannl 数据同步 目录 Cannl 数据同步一、概述1、简介2、原理3、模块 二、配置MySQL1、使用版本使用版本 2、环境要求1)操作系统2)MySQL要求 三、配置Canal-server1、下载安装2、**修改配置****单机配置****集群配置****分库分表配置** 四、配置canal-ada…...
Webpack 前端性能优化全攻略
文章目录 1. 性能优化全景图1.1 优化维度概览1.2 优化效果指标 2. 构建速度优化2.1 缓存策略2.2 并行处理2.3 减少构建范围 3. 输出质量优化3.1 代码分割3.2 Tree Shaking3.3 压缩优化 4. 运行时性能优化4.1 懒加载4.2 预加载4.3 资源优化 5. 高级优化策略5.1 持久化缓存5.2 模…...
时间序列分析的军火库:AutoTS、Darts、Kats、PaddleTS、tfts 和 FancyTS解析
引言:时间序列分析的现代挑战 时间序列分析在多个领域中扮演着关键角色,包括工程、金融、气象、工业预测等。随着开源工具的快速发展,开发者可以通过多种库快速实现时间序列预测与分析。本文将对 AutoTS、Darts、Kats、PaddleTS、tfts 和 FancyTS 六大主流库进行详细解析,…...
MySQL意向锁我该怎么理解?
在MySQL中,意向锁(Intention Lock)是一种用于协调不同粒度锁(如表锁和行锁)的机制,其核心目的是在保证数据一致性的同时提高并发性能。以下是关于意向锁的详细解析: 一、意向锁的作用 意向锁的…...
Linux 操作系统简介
Linux 操作系统 Linux 是一种自由和开源的操作系统,最初由芬兰的 Linus Torvalds 在1991年创建。它是一个类 Unix 操作系统,广泛用于服务器、个人电脑和嵌入式设备。Linux 操作系统的核心是 Linux 内核,其周围构建了各种工具和应用程序&…...
前端大文件上传(分片上传)与下载
文章目录 一、问题二、思路1、选择文件2、校验文件是否符合规范3、文件切片上传4、分片上传注意点5、大文件下载 一、问题 日常业务中难免出现前端需要向后端传输大型文件的情况,这时单次的请求不能满足传输大文件的需求,就需要用到分片上传 业务需求为…...
工业领域 - 离散工业与流程工业极简理解
离散工业 离散工业是指通过组装或加工离散的零部件来生产产品 离散工业生产的是可数的、独立的产品 离散工业的每个产品通常由多个部件组成,生产过程可以分解为多个独立的步骤 离散工业生产过程主要涉及组装和加工,组装是将多个零部件组装成最终产品&…...
一个使用Python和相关深度学习库(如`PyTorch`)实现GCN(图卷积网络)与PPO(近端策略优化)强化学习模型结合的详细代码示例
以下是一个使用Python和相关深度学习库(如PyTorch)实现GCN(图卷积网络)与PPO(近端策略优化)强化学习模型结合的详细代码示例。这个示例假设你在一个图环境中进行强化学习任务。 1. 安装必要的库 确保你已…...
【YOLOv8】YOLOv8改进系列(7)----替换主干网络之LSKNet
主页:HABUO🍁主页:HABUO 🍁YOLOv8入门改进专栏🍁 🍁如果再也不能见到你,祝你早安,午安,晚安🍁 【YOLOv8改进系列】: 【YOLOv8】YOLOv8结构解读…...
CCF CSP 第30次(2023.05)(1_仓库规划_C++)
CCF CSP 第30次(2023.05)(1_仓库规划_C) 题目描述:输入格式:输出格式:样例输入:样例输出:样例解释:子任务:解题思路:思路一࿱…...
【LangChain】理论及应用实战(7):LCEL
文章目录 一、LCEL简介二、LCEL示例2.1 一个简单的示例2.2 RAG Search 三、LCEL下核心组件(PromptLLM)的实现3.1 单链结构3.2 使用Runnables来连接多链结构3.2.1 连接多链3.2.2 多链执行与结果合并3.2.3 查询SQL 3.3 自定义输出解析器 四、LCEL添加Memor…...
Leetcode 刷题笔记1 单调栈part02
leetcode 42 接雨水 本题用双指针法更为浅显易懂 双指针法: class Solution:def trap(self, height: List[int]) -> int:leftheight, rightheight [0] * len(height), [0] * len(height)ans 0leftheight[0] height[0]for i in range(1, len(height)):lefth…...
ai本地化 部署常用Ollama软件
现在用最简单的方式介绍一下 Ollama 的作用和用法: Ollama 是什么? Ollama 是一个让你能在自己电脑上免费运行大型语言模型(比如 Llama 3、Mistral 等)的工具。 相当于你本地电脑上有一个类似 ChatGPT 的 AI,但完全…...
vllm部署QwQ32B(Q4_K_M)
vllm部署QwQ32B(Q4_K_M) Ollama是一个轻量级的开源LLM推理框架,注重简单易用和本地部署,而VLLM是一个专注于高效推理的开源大型语言模型推理引擎,适合开发者在实际应用中集成和使用。两者的主要区别在于Ollama更注重为用户提供多种模型选择和…...
VLLM:虚拟大型语言模型(Virtual Large Language Model)
VLLM:虚拟大型语言模型(Virtual Large Language Model) VLLM指的是一种基于云计算的大型语言模型的虚拟实现。它通常是指那些由多个服务器组成的分布式计算环境中的复杂机器学习模型,这些模型能够处理和理解大量的文本数据。VLLM的…...
