华为OD机试 - 创建二叉树(Java 2024 E卷 200分)

news2025/3/18 6:11:29

题目描述

给定一系列树状结构操作的问题,通过 Q 次查询还原树结构并输出结果。题目要求实现一个类 Solution,其方法 recoverTree 需要根据输入的操作数组 operations 还原树的结构,并返回树的根节点。每个操作 operations[i] = [height, index] 表示在高度为 height 的位置插入一个索引为 index 的节点。

  • 树节点定义

    • 每个节点 node 存在左子节点,则初始为 null
    • 每个节点 node 存在右子节点,则初始为 null
    • 每个节点存储一个高度值 height 和索引值 index
  • 输入要求

    • heightindex 初始为 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。

解题思路

问题分析

题目要求根据一系列操作还原一棵树,并通过查询返回指定节点的值。核心在于:

  1. 树的构建:根据 operations 数组中的 [height, index] 创建节点并插入树中。
  2. 插入规则:高度决定节点的层级,索引决定插入顺序。
  3. 查询操作:根据 [height, index] 找到对应节点并返回其值。
算法选择
  1. 树结构:使用二叉树结构存储节点,每个节点包含 heightindex,并有左右子节点。
  2. 插入规则
    • 如果高度相同,优先插入到左子树。
    • 如果高度更高,优先插入到右子树。
    • 如果高度更低,插入到左子树。
  3. 层次遍历:在查询时,通过层次遍历找到目标节点。
步骤详解
  1. 定义节点类:包含 heightindex、左子节点和右子节点。
  2. 构建树
    • 遍历 operations,对于每个操作:
      • 如果 height 不为 null,创建新节点并插入树中。
      • 插入时,比较 heightindex,决定插入到左子树还是右子树。
  3. 查询节点
    • 遍历 operations,对于查询操作(heightnull),通过层次遍历找到目标节点并返回其值。
  4. 返回结果:按照查询顺序返回结果数组。

代码实现

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 = height
        self.index = index
        self.left = None
        self.right = None

class Solution:
    def recoverTree(self, operations):
        root = None
        result = []
        
        # 构建树
        for height, index in operations:
            if height is not None:
                node = TreeNode(height, index)
                if root is None:
                    root = node
                else:
                    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 result
    
    def insert_node(self, root, node):
        current = root
        while True:
            if node.height == current.height:
                if current.left is None:
                    current.left = node
                    break
                else:
                    current = current.left
            elif node.height > current.height:
                if current.right is None:
                    current.right = node
                    break
                else:
                    current = current.right
            else:
                if current.left is None:
                    current.left = node
                    break
                else:
                    current = current.left
    
    def find_node(self, root, index):
        if not root:
            return None
        from collections import deque
        queue = deque([root])
        
        while queue:
            node = queue.popleft()
            if node.index == index:
                return node
            if 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。

问题总结

本题是一个树结构的构建和查询问题,核心在于根据高度和索引的规则插入节点,并通过层次遍历查询目标节点。算法的关键点包括:

  1. 插入规则:高度决定插入方向,相同高度优先左子树。
  2. 查询效率:通过层次遍历查找目标节点。
  3. 边界处理:处理 null 查询和未找到节点的情况。

该算法适用于动态构建树并查询的场景,但在树退化为链时效率较低。可能的优化方向包括使用平衡树(如 AVL 树或红黑树)来降低树高,从而将时间复杂度优化到 O(Q * log Q)。在实际应用中,例如文件系统或组织结构管理,可以用类似方法动态构建和查询树结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++基础知识-图论进阶

一、拓扑排序 1、基础知识 1&#xff09;什么是拓扑排序 对一个有向无环图G进行拓扑排序&#xff0c;是将G中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点u和v&#xff0c;若&#xff0c;则u在线性序列中出现在v之前。 2&#xff09;拓扑排序的操作方法 重复执行…

[Java实战]Spring Boot服务CPU 100%问题排查:从定位到解决

Spring Boot服务CPU 100%问题排查&#xff1a;从定位到解决 1. 引言 当Spring Boot服务出现CPU占用率100%时&#xff0c;系统性能会急剧下降&#xff0c;甚至导致服务不可用。本文将通过真实代码案例&#xff0c;详细讲解如何快速定位问题根源&#xff0c;并提供解决方案。无…

1.6 极限存在准则

1.夹逼定理&#xff08;迫敛定理&#xff09; 1.1 数列型 1.1.1 准则 1.2 函数型 2. 两个重要极限

大华SDK协议在智联视频超融合平台中的接入方法

一. 大华SDK协议详解 &#xff08;一&#xff09;、大华SDK协议概述 大华SDK&#xff08;Software Development Kit&#xff09;协议是大华股份为开发者提供的一套软件开发工具包&#xff0c;旨在帮助开发者快速集成大华设备&#xff08;如摄像头、NVR、DVR等&#xff09;的功…

卓越的用户体验需要智能内容

摘要&#xff1a;这篇文章指出静态文档已无法满足现代用户的需求&#xff0c;而智能内容则是构建卓越用户体验的关键。文章从智能内容的定义、优势和实际应用等方面进行了详细阐述&#xff0c;并强调了企业应积极拥抱智能内容&#xff0c;以提升客户满意度、降低成本并创造新的…

【蓝桥杯】1124修建公路1(Kruskal算法)

思路 找到能够连通所有城市的最小树即可&#xff0c;可用Prim或Kruscal。 &#xff01;&#xff01;注意&#xff0c;m的范围是包括0的&#xff0c;可就是包含没有道路的情况&#xff0c;要单独输出0 code import os import sys# 输入 n,m map(int,input().split()) road …

传感云揭秘:边缘计算的革新力量

在当今快速发展的科技时代&#xff0c;传感云和边缘计算系统正逐渐成为人们关注的焦点。传感云作为物联网与云计算的结合体&#xff0c;通过虚拟化技术将物理节点转化为多个服务节点&#xff0c;为用户提供高效、便捷的服务。而边缘计算则是一种靠近数据源头或物端的网络边缘侧…

Bigemap Pro 的三种地图下载方式

地图下载通常是是最基础但也最重要的任务之一&#xff0c;无论是进行空间分析、制作专题地图&#xff0c;还是进行数据可视化&#xff0c;高质量的地图数据都是不可或缺的。Bigemap Pro提供了三种地图下载方式&#xff0c;分别适用于不同的场景和需求。无论是免费版用户还是专业…

Python直方图:从核密度估计到高维空间解析

一、直方图的核心原理与数学本质 数据分布的视觉解码器 直方图&#xff08;Histogram&#xff09;是数据科学家的"分布显微镜"&#xff0c;通过将连续数据划分为等宽区间&#xff08;Bin&#xff09;&#xff0c;统计各区间的频数/频率&#xff0c;用相邻矩形条直观…

0基础 | 恒流源专题

目录 tip1&#xff1a;低端反馈​编辑 tip2: 恒流源电路的设计注意事项 tip3&#xff1a;三极管输出恒定电流受运放输出电流控制 tip4&#xff1a;高端反馈 基本逻辑&#xff1a; 当负端Vref不输入电压时&#xff0c; 当负端Vref输入电压时 tip1&#xff1a;低端反馈 判…

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 模…

【YOLOv8】YOLOv8改进系列(7)----替换主干网络之LSKNet

主页&#xff1a;HABUO&#x1f341;主页&#xff1a;HABUO &#x1f341;YOLOv8入门改进专栏&#x1f341; &#x1f341;如果再也不能见到你&#xff0c;祝你早安&#xff0c;午安&#xff0c;晚安&#x1f341; 【YOLOv8改进系列】&#xff1a; 【YOLOv8】YOLOv8结构解读…

【LangChain】理论及应用实战(7):LCEL

文章目录 一、LCEL简介二、LCEL示例2.1 一个简单的示例2.2 RAG Search 三、LCEL下核心组件&#xff08;PromptLLM&#xff09;的实现3.1 单链结构3.2 使用Runnables来连接多链结构3.2.1 连接多链3.2.2 多链执行与结果合并3.2.3 查询SQL 3.3 自定义输出解析器 四、LCEL添加Memor…

ai本地化 部署常用Ollama软件

现在用最简单的方式介绍一下 Ollama 的作用和用法&#xff1a; Ollama 是什么&#xff1f; Ollama 是一个让你能在自己电脑上免费运行大型语言模型&#xff08;比如 Llama 3、Mistral 等&#xff09;的工具。 相当于你本地电脑上有一个类似 ChatGPT 的 AI&#xff0c;但完全…

vllm部署QwQ32B(Q4_K_M)

vllm部署QwQ32B(Q4_K_M) Ollama是一个轻量级的开源LLM推理框架&#xff0c;注重简单易用和本地部署&#xff0c;而VLLM是一个专注于高效推理的开源大型语言模型推理引擎&#xff0c;适合开发者在实际应用中集成和使用。两者的主要区别在于Ollama更注重为用户提供多种模型选择和…

企业内网监控软件的选型与应用:四款主流产品的深度剖析

在数字化办公的时代背景下&#xff0c;企业内部网络管理的重要性愈发显著。对于企业管理者而言&#xff0c;如何精准掌握员工工作状态&#xff0c;保障网络安全与工作效率&#xff0c;已成为亟待解决的关键问题。本文将深入剖析四款主流企业内网监控软件&#xff0c;探讨其功能…

Qt窗口控件之字体对话框QFontDialog

字体对话框QFontDialog QFontDialog 是 Qt 内置的字体对话框&#xff0c;用户能够在这里选择字体的样式、大小&#xff0c;设置加粗和下划线并将结果作为返回值返回。QFontDialog 最好使用其提供的静态函数实例化匿名对象&#xff0c;并获取返回值最为用户选择字体设置的结果。…

Qt QML实现视频帧提取

## 前言 视频帧率&#xff08;Frame Rate&#xff09;是指视频播放时每秒显示的画面帧数&#xff0c;通常用fps&#xff08;Frames Per Second&#xff09;来表示。视频是由一系列静止的图像帧组成的&#xff0c;而视频帧率则决定了这些图像帧在单位时间内播放的速度。较高的视…

在 Ubuntu 服务器上使用宝塔面板搭建博客

&#x1f4cc; 介绍 在本教程中&#xff0c;我们将介绍如何在 Ubuntu 服务器 上安装 宝塔面板&#xff0c;并使用 Nginx PHP MySQL 搭建一个博客&#xff08;如 WordPress&#xff09;。 主要步骤包括&#xff1a; 安装宝塔面板配置 Nginx PHP MySQL绑定域名与 SSL 证书…

有了大语言模型还需要 RAG 做什么

一、百炼平台简介 阿里云的百炼平台就像是一个超级智能的大厨房&#xff0c;专门为那些想要做出美味AI大餐的企业和个人厨师准备的。你不需要从头开始做每一道菜&#xff0c;因为这个厨房已经为你准备了很多预制食材&#xff08;预训练模型&#xff09;&#xff0c;你可以根据…