欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > 【华为OD】2024D卷——生成哈夫曼树

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

2025/1/20 14:24:23 来源:https://blog.csdn.net/weixin_45653183/article/details/142178383  浏览:    关键词:【华为OD】2024D卷——生成哈夫曼树
题目描述:
给定长度为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 = weightself.left = leftself.right = rightself.height = 1#   递归计算节点的高度def get_height(self):if not self.left and not self.right:return 1left_height = self.left.get_height() if self.left else 0right_height = self.right.get_height() if self.right else 0return 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:returnstack = [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:returndfs(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个叶节点的森林开始,每个叶节点代表一个字符及其出现的频率。

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

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

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


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


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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com