【华为OD】2024D卷——生成哈夫曼树

news2024/11/15 18:14:28
题目描述:
给定长度为n的无序的数字数组,每个数字代表二叉树的叶子节点的权值,数字数组的值均大于等于1。
请完成一个函数,根据输入的数字数组,生成哈夫曼树,并将哈夫曼树按照中序遍历输出。

为了保证输出的二叉树中序遍历结果统一,增加以下限制:
二叉树节点中,左节点权值小于等于右节点权值,
根节点权值为左右节点权值之和。
当左右节点权值相同时,左子树高度高度小于等于右子树。

注意:所有用例保证有效,并能生成哈夫曼树。

提醒:哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度
(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。

解题思路:

由于未找到输入输出示例,自己手动创建一个输入权重列表weights = [5,5,15,40,30,10]

根据题意中,哈夫曼树的要求,可以构造树形如下:

故中序遍历结果[40, 105, 30, 65, 15, 35, 10, 20, 5, 10, 5]

构造哈夫曼树

1、创建一个优先级队列(最小堆)并将数组中的每个叶子节点作为一个节点加入堆中。

由于直接将节点压入最小堆中,使其只基于节点的权重进行比较。需要在树节点中重写类的 __lt__() 方法

解决可以解决heapq 无法比较 Node 实例的问题,否则会遇见报错TypeError: '<' not supported between instances of 'TreeNode' and 'TreeNode'。

2、不断从堆中取出两个最小的节点作为左右子树,构造一个新的父节点,其权值为两个节点权值之和,并将新节点插入堆中。

3、重复此过程,直到堆中只剩下一个节点,即为哈夫曼树的根节点。

对限制条件进行处理

1、在合并左右节点时,如果两个节点的权值相等,则优先选择左子树高度小于或等于右子树的情况。

故需要统计每个节点的高度,可以在树节点类型中添加height值。

2、根节点权值为左右节点权值之和。

在构造时,父节点权值 = left_weight + right_weight,重新压入heapq中。

3、如果左右节点的权值不等,左子树权值必须小于等于右子树权值。

heapq弹出的第一个元素为左孩子、第二个为右孩子。

中序遍历输出

使用递归进行中序遍历:左子树 -> 根节点 -> 右子树。


代码部分

class TreeNode:
    def __init__(self, weight, left = None, right = None):
        self.weight = weight
        self.left = left
        self.right = right
        self.height = 1
    #   递归计算节点的高度
    def get_height(self):
        if not self.left and not self.right:
            return 1
        left_height = self.left.get_height() if self.left else 0
        right_height = self.right.get_height() if self.right else 0
        return 1 + max(left_height, right_height)
    #   重写方法,基于节点的权重进行比较。这解决了 heapq 无法比较 Node 实例的问题
    def __lt__(self, other):
        return self.weight < other.weight
import heapq
class Solution:
    def build_huffman_tree(self, weights):
        if not weights:
            return 
        # 初始化最小堆,创建叶子节点
        heap = [TreeNode(weight) for weight in weights]
        heapq.heapify(heap)
        while len(heap) > 1:
            # 取出两个权值最小的节点
            left = heapq.heappop(heap)
            right = heapq.heappop(heap)
            # 如果权值相同,按照高度进行处理
            if left.weight == right.weight:
                left_height = left.get_height()
                right_height = right.get_height()
                # 确保左子树高度 <= 右子树高度
                if left_height > right_height:
                    left, right = right, left
            # 创建新的父节点,权值为左右节点权值之和
            merged_node = TreeNode(left.weight + right.weight, left, right)
            heapq.heappush(heap, merged_node)
        # 返回堆中唯一的节点,即哈夫曼树的根节点
        return heap[0]
    #   栈迭代法进行中序遍历
    def midorder_traversal(self, root:TreeNode):
        res = []
        if root is None:
            return
        stack = [root]
        while stack:
            node = stack.pop()
            if node != None:
                #   按照顺序压栈:右节点、中节点、左节点
                if node.right:
                    stack.append(node.right)
                stack.append(node)
                #   中节点访问过,但是还没有处理,加入空节点做为标记。
                stack.append(None)
                if node.left:
                    stack.append(node.left)
            #  只有遇到空节点的时候,才将下一个节点放进结果集
            else:
                #   按照左节点、中节点、右节点出栈
                node = stack.pop()
                res.append(node.weight)
        return res
    #   递归法进行中序遍历
    def midorder_traversal_I(self, root:TreeNode):
        res = []
        def dfs(node):
            if node is None:
                return
            dfs(node.left)
            res.append(node.weight)
            dfs(node.right)
        dfs(root)
        return res
    #   递归法简化写法
    def midorder_traversal_II(self, root:TreeNode):
        if root is None:
            return []
        return self.midorder_traversal_II(root.left) + [root.weight] + self.midorder_traversal_II(root.right)

if __name__=='__main__':
    weights = list(map(int, input().split(',')))
    solution = Solution()
    huffman_tree = solution.build_huffman_tree(weights)
    result = solution.midorder_traversal(huffman_tree)
    print(result)

拓展:

__lt__() 方法是一个特殊方法(也称为魔术方法或双下划线方法),它用于定义小于(<)运算符的行为。

在尝试比较两个类的实例时,如果这两个实例之间的比较逻辑不是简单的基于它们的值或身份(即不是直接通过 ==!=<<=>>= 这样的操作符),就可以通过定义这些特殊方法来自定义这些操作符的行为。

__lt__() 方法应该接收一个参数(即与当前实例进行比较的另一个实例),并返回一个布尔值:如果当前实例小于传入的参数,则返回 True;否则返回 False。

哈夫曼树的特点:

带权路径长度(WPL, Weighted Path Length)最小

·对于一组给定的字符及其出现的频率(即权重),哈夫曼树能够构造出一种编码方式,使得这些字符的平均编码长度最短。

·这是通过构建一种特殊的二叉树来实现的,其中每个字符都位于树的一个叶节点上,且该叶节点到根节点的路径上的边所代表的字符(在哈夫曼编码中通常使用0和1表示)共同构成了该字符的编码。

贪心算法构建

·哈夫曼树的构建过程采用了贪心算法的策略。

·它从包含n个叶节点的森林开始,每个叶节点代表一个字符及其出现的频率。

·然后,算法反复地选择两个频率最小的节点,将它们合并为一个新的节点,新节点的频率是两个子节点频率之和。

·新节点作为这两个子节点的父节点,而原来的两个节点则成为新节点的左右子节点(通常约定左子节点频率小于等于右子节点)。

·这个过程一直进行,直到森林中只剩下一个节点为止,这个节点即为根节点,整棵树就是所求的哈夫曼树。


知识点:哈夫曼树、堆、二叉树的中序遍历、贪心


结语:越简单的题目解法应该越多,请路过大神留下新的思路供本小白学习一下,打开思路

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

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

相关文章

PowerShell install 一键部署Oracle19c

Oracle19c前言 Oracle 19c 是甲骨文公司推出的一款企业级关系数据库管理系统,它带来了许多新的功能和改进,使得数据库管理更加高效、安全和可靠。以下是关于 Oracle 19c 的详细介绍: 主要新特性 多租户架构:支持多租户架构,允许多个独立的数据库实例在同一个物理服务器上…

【PLW003】设备器材云端管理平台v1.0(SpringBoot+Mybatis+NodeJS+MySQL前后端分离)

设备器材云端管理平台是一种专为各种设备&#xff08;如教育行业中的实验设备、建筑行业中的施工设备等&#xff09;租赁或共享孵化的数字化管理工具&#xff0c;旨在融合数字化手段&#xff0c;提高各种设备器材的管理效率、 确保设备的安全稳定运行&#xff0c;并优化资源使用…

分布式事务学习笔记(五)微服务实现Seata TCC模式、TC服务器高可用异地容灾

文章目录 前言6 Seata TCC 模式6.1 实现原理6.2 优缺点6.3 空回滚和业务悬挂6.3.1 空回滚6.3.2 业务悬挂 6.4 微服务实现TCC模式6.4.1 思路分析6.4.2 声明TCC接口6.4.3 编写实现类6.4.4 Controller类调用TCC接口6.4.5 修改配置文件application.yml6.4.6 重启微服务并测试 7 TC服…

【Linux】常用指令(中)(附带基础指令的详细讲解、Linux的一些附加知识)

文章目录 前言1. Linux基础常用指令1.1 通配符 "*"1.2 man指令&#xff08;重要&#xff09;1.2.1 man指令的语法 1.3 何为"指令"&#xff1f;(附带知识)1.4 echo指令1.5 cat指令1.6 Linux下一切皆文件&#xff01;1.6.1 ">" 输出重定向1.6.2…

FP6296XR-G1 10A电流模式非同步PWM升压转换器芯片IC

一般说明 F1 6296是目前最先进的直流一直流转换器。是一个带有内置15mΩ功率MOSFET使此稳压器具有高功率效率。误差放大器的非逆变输入端连接到1.2V的精密基准电压。电流模式控制和外部补偿网络使系统稳定容易灵活。FP6296采用SOP-8L(EP)封装&#xff0c;可用于应用领域…

基于 K8S kubernetes 搭建 安装 EFK日志收集平台

目录 1、在k8s中安装EFK组件 1.1 安装elasticsearch组件 1.2 安装kibana组件 1.3 安装fluentd组件 文档中的YAML文件配置直接复制粘贴可能存在格式错误&#xff0c;故实验中所需要的YAML文件以及本地包均打包至网盘 链接&#xff1a;https://pan.baidu.com/s/15Ryaoa0_…

各大平台统遭入侵??区块链市场遭攻击损失近3亿!

今年&#xff0c;全球发生多起骇人听闻的勒索入侵软件攻击事件&#xff0c;黑客组织利用各种手段和技术&#xff0c;不断试图突破网络安全防线&#xff0c;窃取敏感信息、破坏系统运行&#xff0c;甚至进行勒索和敲诈&#xff0c;使得网络安全问题日益凸显其重要性和紧迫性。 S…

【北京迅为】《STM32MP157开发板使用手册》- 第三十五章 A7 和 M4 联合调试

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

【MySQL】表的操作【有关表结构的操作】【创建、查看、删除、修改表结构】

目录 表的操作1.创建表2.查看表结构3修改表3.1修改表名3.2添加字段/列3.3修改字段/列3.4删除字段/列3.5对单一字段/列 重命名 4.删除表 表的操作 1.创建表 创建表的语句语法&#xff1a; CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) ch…

基于SSM的在线家用电器销售系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSSMVueMySQL的在线家…

【学习笔记】SSL/TLS安全机制之HSTS

1、What&#xff1a;HSTS全称HTTP Strict Transport Security&#xff0c;HTTP严格传输安全。 2、Why&#xff1a;因为HTTP 在重定向到 HTTPS 之前存在漏洞 用户在浏览时很少明确输入 https:// 有时&#xff0c;用户正好通过http来访问网站&#xff0c;网站服务器知道这应该是…

43.哀家要长脑子了!

1.53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int maxSubArray(vector<int>& nums) {int pre 0, maxRes nums[0];for(int x : nums){pre max(pre x, x);maxRes max(maxRes, pre); }return maxRes;} }; 其实弄懂也就挺简…

初写MySQL四张表:(3/4)

我们已经完成了四张表的创建&#xff0c;学会了创建表和查看表字段信息的语句。 初写MySQL四张表:(1/4)-CSDN博客 初写MySQL四张表:(2/4)-CSDN博客 接下来&#xff0c;我们来学点对数据的操作&#xff1a;增 删 查&#xff08;一部分&#xff09;改 先来看这四张表以及相关…

python-简单的数据结构

题目描述 小理有一天在网上冲浪的时候发现了一道很有意思的数据结构题。 该数据结构形如长条形。 一开始该容器为空&#xff0c;有以下七种操作。 1 a从前面插入元素 a ; 2 从前面删除一个元素; 3 a从后面插入一个元素; 4 从后面删除一个元素; 5 将整个容器头尾翻转; 6 输出个…

存储数据的树形结构

目录 1、二叉查找树 2、平衡二叉树AVL Tree 3 、平衡多叉树B-Tree 4、BTree树 5 、红黑树 红黑树的应用 6.平衡树的旋转 mysql 索引数据结构&#xff1a; Btree 索引是B树在数据库中的一种实现&#xff0c;最为常见的。B树 中的B代表平衡&#xff0c;而不是二叉 1、二…

火山引擎数智平台:高性能ChatBI的技术解读和落地实践

导读&#xff1a;大模型能力的发展和成熟&#xff0c;催生出新一代智能化 BI—— ChatBI&#xff0c;即通过自然语言处理&#xff08;NLP&#xff09;与大型语言模型&#xff08;LLMs&#xff09;的结合&#xff0c;极大简化数据分析过程&#xff0c;提高效率并降低分析门槛。火…

剪画:视频怎么去水印?分享几个简单实用的视频去水印方法!

亲爱的小伙伴们&#xff0c;在视频创作的道路上&#xff0c;水印问题是不是常常让你感到困扰呢&#xff1f; 别担心&#xff0c;今天就来为大家详细介绍七种超实用的视频去水印方法&#xff0c;让你的视频制作更加顺畅。 一、剪画 - 短视频去水印 剪画是一款非常强大的视频处理…

双向NAT=源NAT+NAT Server,有这么6?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 你们好&#xff0c;我的网工朋友。 随着移动设备的普及和云计算技术的发展&#xff0c;网络流量的规模和复杂度不断增加。网络地址转换&#xff…

像JSON一样使用ProtoBuf,空间还能缩小60%,性能提升100%

首发公众号:【赵侠客】 引言 在前面《释放你九成的带宽和内存&#xff1a;GZIP在解决Redis大Key方面的应用》一文中我使用GZIP算法可以将JSON格式数据的大小缩小88%从而节省了大量的存储和带宽资源&#xff0c;本文介绍另一种JAVA对象序列化神器——ProtoBuf&#xff08;Proto…