当前位置: 首页 > news >正文

华为OD机试真题---生成哈夫曼树

华为OD机试中关于生成哈夫曼树的题目通常要求根据给定的叶子节点权值数组,构建一棵哈夫曼树,并按照某种遍历方式(如中序遍历)输出树中节点的权值序列。以下是对这道题目的详细解析和解答思路:

一、题目要求

给定一个长度为n的正整数数组,每个数字代表二叉树叶子节点的权值。要求生成一棵哈夫曼树,并将其按中序遍历的顺序输出。树中每个非叶子节点的权值等于其左右子节点权值之和。对于权值相同的两个节点,左子树的高度应小于等于右子树的高度。在满足上述条件的前提下,左子节点的权值应小于等于右子节点的权值。

二、哈夫曼树简介

哈夫曼树是一种带权最优二叉树,其特点是带权路径长度最短。所谓带权路径长度,是指树中所有叶子节点的权值乘上其到根节点的路径长度(若根节点为0层,则叶子节点到根节点的路径长度为叶子节点的层数)之和。

三、解题思路

  1. 构建森林:将每个叶子节点看作一棵独立的树,并将这些树放入一个优先队列(最小堆)中。优先队列用于每次选择权值最小的两个节点进行合并。
  2. 合并节点:从优先队列中取出权值最小的两个节点,将它们作为新树的左右子树,并计算新树的权值为两棵子树权值之和。然后,将新树加入优先队列中。
  3. 重复合并:重复上述步骤,直到优先队列中只剩下一棵树,这棵树即为所求的哈夫曼树。
  4. 中序遍历:对构建好的哈夫曼树进行中序遍历,得到节点权值序列,并按要求输出。

四、代码实现

import java.util.*;class HuffmanNode implements Comparable<HuffmanNode> {int weight;HuffmanNode left, right;HuffmanNode(int weight) {this.weight = weight;this.left = this.right = null;}@Overridepublic int compareTo(HuffmanNode other) {return this.weight - other.weight;}
}public class HuffmanTree {/*** 构建哈夫曼树* 哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树* 在数据压缩、编码等领域有广泛的应用** @param weights 每个节点的权重,权重越小的节点,在构建哈夫曼树时,越靠近根节点* @return 返回哈夫曼树的根节点*/public static HuffmanNode buildHuffmanTree(int[] weights) {// 优先队列,用于存储哈夫曼树的节点,队列头部元素权值最小PriorityQueue<HuffmanNode> pq = new PriorityQueue<>();// 遍历所有节点权重,初始化哈夫曼树节点并加入优先队列for (int weight : weights) {pq.add(new HuffmanNode(weight));}// 当优先队列中节点数量大于1时,循环构建哈夫曼树while (pq.size() > 1) {// 从优先队列中取出权值最小的两个节点HuffmanNode left = pq.poll();HuffmanNode right = pq.poll();// 断言right节点非空,确保树能正常构建assert right != null;// 创建新的哈夫曼节点,作为left和right节点的父节点,其权重为两个子节点权重之和HuffmanNode parent = new HuffmanNode(left.weight + right.weight);// 设置父节点的左右子节点parent.left = left;parent.right = right;// 将新的父节点加入优先队列pq.add(parent);}// 返回优先队列中剩余的唯一节点,即哈夫曼树的根节点return pq.poll();}/*** 中序遍历哈夫曼树* 中序遍历的特点是左子树-根节点-右子树的顺序访问树中的节点* 这种遍历顺序在哈夫曼树中主要用于验证树的结构,而不用于构建最短编码** @param root 哈夫曼树的根节点* @param result 用于存储遍历结果的列表,通常是节点的权重*/public static void inorderTraversal(HuffmanNode root, List<Integer> result) {// 判断当前节点是否为空,为空则直接返回if (root == null) {return;}// 递归中序遍历左子树inorderTraversal(root.left, result);// 将当前节点的权重添加到结果列表中result.add(root.weight);// 递归中序遍历右子树inorderTraversal(root.right, result);}/*** 主函数入口* 本程序用于演示霍夫曼树的构建及其遍历过程* 霍夫曼树是一种带权路径长度最短的二叉树,具有重要应用价值** @param args 命令行参数*/public static void main(String[] args) {// 创建Scanner对象,用于读取命令行输入Scanner scanner = new Scanner(System.in);// 读取霍夫曼树节点数量int n = scanner.nextInt();// 初始化霍夫曼树节点权重数组int[] weights = new int[n];// 读取每个节点的权重for (int i = 0; i < n; i++) {weights[i] = scanner.nextInt();}// 构建霍夫曼树并返回根节点HuffmanNode root = buildHuffmanTree(weights);// 初始化用于存储中序遍历结果的列表List<Integer> result = new ArrayList<>();// 中序遍历霍夫曼树,并将结果存储到列表中inorderTraversal(root, result);// 输出中序遍历结果for (int weight : result) {System.out.print(weight + " ");}}
}

五、代码解析

  1. HuffmanNode 类

    • HuffmanNode 是一个表示哈夫曼树节点的类。
    • 它包含三个属性:weight(节点权重),left(左子节点),right(右子节点)。
    • 实现了 Comparable<HuffmanNode> 接口,以便节点可以根据权重进行排序。
  2. HuffmanTree 类

    • 包含构建哈夫曼树的静态方法 buildHuffmanTree
    • 包含进行中序遍历的静态方法 inorderTraversal
    • main 方法用于读取输入、构建哈夫曼树、进行中序遍历并输出结果。
  3. main 方法

    • 读取节点数量 n 和每个节点的权重。
    • 调用 buildHuffmanTree 方法构建哈夫曼树。
    • 初始化结果列表 result 并调用 inorderTraversal 方法进行中序遍历。
    • 输出遍历结果。

运行实例解析

输入

5
5 15 40 30 10

步骤

  1. 读取输入

    • 节点数量 n = 5
    • 节点权重数组 weights = [5, 15, 40, 30, 10]
  2. 构建哈夫曼树

    • 初始化优先队列 pq 并添加所有节点。
    • 不断从 pq 中取出两个最小权重的节点,合并成一个新节点,并将新节点添加回 pq
    • 最终,pq 中剩下的唯一节点即为哈夫曼树的根节点。

    构建过程(可能的一种情况,因为合并顺序可能不同):

    • 初始:pq = [5, 10, 15, 30, 40]
    • 合并 510,得到新节点 15,加入 pqpq = [15, 15, 30, 40]
    • 合并两个 15,得到新节点 30,加入 pqpq = [30, 30, 40]
    • 合并 3030,得到新节点 60,加入 pqpq = [60, 40]
    • 合并 6040,得到根节点 100
  3. 中序遍历

    • 由于哈夫曼树不是二叉搜索树,中序遍历的结果取决于节点的合并顺序和树的结构。
    • 假设按上述构建过程,中序遍历结果可能是一种特定的节点权重顺序(注意,这不是唯一的)。
  4. 输出结果

    • 输出中序遍历得到的节点权重列表。

注意:由于哈夫曼树的构建过程中节点合并顺序可能不同(当存在相同权重的节点时),因此中序遍历的结果也可能不同。上述构建过程和中序遍历结果是基于一种假设的合并顺序。

六、实际输出

由于代码中的中序遍历会按照左子树-根节点-右子树的顺序访问节点,并且哈夫曼树的构建依赖于节点的合并顺序,因此实际输出将取决于具体的合并过程。要获得确切的输出,需要运行代码并观察结果。

如果您想要验证哈夫曼树的正确性,通常不会使用中序遍历,而是会检查每个节点的权重和从根到该节点的路径长度(用于计算哈夫曼编码的长度等)。中序遍历在这里主要用于演示和验证树的结构。

http://www.lryc.cn/news/460407.html

相关文章:

  • 小红书新ID保持项目StoryMaker,面部特征、服装、发型和身体特征都能保持一致!(已开源)
  • Docker 环境下 GPU 监控实战:使用 Prometheus 实现 DCGM Exporter 部署与 GPU 性能监控
  • 联想小新打印机M7328w如何解决卡纸,卡了一个小角在里面,然后再次打印的时候,直接卡住,不能动了。灯显示红色。
  • 软件可靠性之MTTR、MTBF、MTTF、MTTD区别
  • Qt-QDockWidget浮动窗口相关操作(49)
  • 图形用户界面-GUI的基本概念和组件之一
  • 【MATLAB代码】基于RSSI原理的蓝牙定位程序(N个锚点、三维空间),源代码可直接复制
  • Pyenv 介绍和安装指南 - Ubuntu 24
  • zookeeper实现RMI服务,高可用,HA
  • 通过Express + Vue3从零构建一个用户认证与授权系统(一)项目结构设计
  • JavaScript 第13章:Ajax 与异步请求
  • 速卖通商品详情接口技术解析及Python代码示例
  • 邻接表的有向网(C语言代码)
  • 大模型生成PPT大纲优化方案:基于 nVidia NIM 平台的递归结构化生成
  • MRSO算法(JCR2区)
  • 最新Spring Boot3框架入门教程,基础知识讲解(参考官方文档),同时基于MybatisPlus+MYSQL搭建后台管理系统基础流程(附源码)
  • 导数的概念及在模型算法中的应用
  • 获取首日涨停封盘后第二次交易日上涨/下跌的概率
  • shell $ 用法
  • 如何用支付宝实现靠脸吃饭
  • Visual Studio的实用调试技巧总结
  • graphrag学习总结
  • 专题:贪心算法(已完结)
  • Hadoop的三种运行模式:单机模式、伪分布式模式和完全分布式模式
  • JavaScript将array数据下载到Excel中
  • 【前端】Bootstrap:快速开始
  • 文献阅读(222) VVQ协议死锁
  • Node.js管理工具NVM
  • 云原生后端
  • 充电宝哪个品牌值得买?2024年五款靠谱充电宝推荐