• 2. 计算WPL


    题目

    Huffman编码是通信系统中常用的一种不等长编码,它的特点是:能够使编码之后的电文长度最短。

    更多关于Huffman编码的内容参考教材第十章。

    输入:
        第一行为要编码的符号数量n
        第二行~第n+1行为每个符号出现的频率

    输出:
        对应哈夫曼树的带权路径长度WPL

    解释

    ①哈夫曼树的构造

    哈夫曼树,也称为最优二叉树,是一种带权路径长度(WPL)最短的二叉树。这里的带权路径长度就是叶子节点的权值与它到根节点的路径长度之积的总和。

    哈夫曼树的构造方法是基于贪心算法,步骤如下:

    1. 将每个权值看作是独立的一棵树,这些权值通常来自要编码字符的频率。

    2. 在所有未构造二叉树的集合中选出两个权值最小的树作为左右子树,构造出一棵新的二叉树,同时这两个权值之和作为新的父节点的权值。

    3. 在集合中删除这两个已经被使用的节点,将新构造的二叉树(父节点)加入到集合中。

    4. 重复其中的步骤2-3,直到集合中只剩下一棵树,这棵树就是最终的哈夫曼树。

    哈夫曼树常见的应用是哈夫曼编码,它是一种被广泛用于数据压缩的编码方式。根据哈夫曼树,我们可以为叶子节点分配相应的哈夫曼编码,使得编码长度短的更常见,这样可以有效地减少编码的总长度,达到数据压缩的目的。

    ②WPL的计算

    带权路径长度(Weighted Path Length,简称 WPL),它是所有叶子节点的权值乘以其到根节点的路径长度之和。

    以二叉树为例,叶子节点是没有子节点的节点,而根节点是最顶层的节点。路径长度则是从一个节点到另一个节点之间的边的数量。

    具体计算过程如下:

    1. 对每个叶子节点,计算根节点到该叶子节点的路径长度,即从根节点到叶子节点所经过的边的数量。

    2. 将每个叶子节点的权值与其对应的路径长度相乘。

    3. 将上述所有的乘积相加,得到的总和就是这棵树的带权路径长度。

    在哈夫曼编码中,我们通常希望带权路径长度尽可能的小,这样可以让编码更加高效。

    C++代码

    1. #include
    2. #include
    3. using namespace std;
    4. struct Node { // 定义节点结构体
    5. int freq; // 频率
    6. Node* left; // 左子节点
    7. Node* right; // 右子节点
    8. };
    9. // 创建新的节点
    10. Node* newNode(int freq) // 定义新节点的创建函数,输入是频率值,返回创建的新节点的指针
    11. {
    12. Node* node = new Node(); // 动态创建新节点
    13. node->left = node->right = NULL; // 初始化左右子节点为空
    14. node->freq = freq; // 设置新节点的频率
    15. return (node); // 返回新节点的指针
    16. }
    17. // 比较节点的频率
    18. struct compare { // 定义比较结构体,作为优先级队列的比较函数
    19. bool operator()(Node* l, Node* r) // 重载括号运算符,用以比较两个节点的频率
    20. {
    21. return (l->freq > r->freq); // 如果第一个节点频率大于第二个,返回真,否则假
    22. }
    23. };
    24. // 计算哈夫曼树的权值路径长度
    25. int calculateWPL(Node* root, int depth = 0) // 定义计算WPL的函数,输入是根节点的指针和路径深度(默认为0),返回路径长度值
    26. {
    27. if (!root) return 0; // 如果节点为空,返回0
    28. if (!root->left && !root->right) return depth * root->freq; // 如果是叶子节点(无左右子节点),返回当前深度乘以节点频率
    29. // 如果不是叶子节点,递归计算左右子树的WPL,返回两者之和
    30. return calculateWPL(root->left, depth + 1) + calculateWPL(root->right, depth + 1);
    31. }
    32. int main() {
    33. int n;
    34. cin >> n;
    35. priority_queue, compare> pq; // 定义一个优先队列,用于存储节点指针,利用 compare 结构体比较节点频率
    36. for (int i = 0; i < n; ++i)
    37. {
    38. int freq;
    39. cin >> freq; // 输入每个节点的频率值
    40. pq.push(newNode(freq)); // 创建新的节点,并将其添加到优先队列中
    41. }
    42. while (pq.size() != 1) { // 当优先队列中只有一个元素时,结束循环
    43. Node* left = pq.top(); // 获取频率最小的节点,作为左子节点
    44. pq.pop(); // 将该节点从优先队列中移除
    45. Node* right = pq.top(); // 获取频率次小的节点,作为右子节点
    46. pq.pop(); // 将该节点从优先队列中移除
    47. int sum = left->freq + right->freq; // 计算左右子节点频率之和
    48. Node* top = newNode(sum); // 以这个频率和创建新节点
    49. top->left = left; // 将左子节点连接到新节点
    50. top->right = right; // 将右子节点连接到新节点
    51. pq.push(top); // 将新节点添加到优先队列中
    52. }
    53. cout << "WPL=" << calculateWPL(pq.top()) << endl; // 输出哈夫曼树的带权路径长度
    54. return 0; // 程序执行成功,返回0
    55. }

  • 相关阅读:
    AI绘图开源工具Stable Diffusion WebUI前端API对接
    数电实验-----实现74LS153芯片扩展为8选1数据选择器以及应用(Quartus II )
    从固定管线到可编程管线:十段代码入门OpenGL
    java计算机毕业设计ssm+vue红联小区果蔬销售网站-水果购物商城
    《web课程设计》用HTML CSS做一个简洁、漂亮的个人博客网站
    第9讲:VUE中监听器WATCH使用详解
    【VisualStudio 】VisualStudio2022 项目模板
    Vue模板语法
    表格制作软件排行榜,热门做表格的软件推荐
    Nestjs配置服务,配置Cookie和Session
  • 原文地址:https://blog.csdn.net/m0_74200772/article/details/134007517