• 实验5 二叉树的应用程序设计


    实验预备知识:

    1.掌握二叉树的创建和遍历算法。

    2.掌握哈夫曼编码原理。

    一、实验目的

    1.进一步掌握二叉树的存储结构和相应算法。

    2.掌握哈夫曼树树的创建和哈夫曼编码。

    二、实验环境

    ⒈ 硬件:每个学生需配备计算机一台。操作系统:Windows

    ⒉ 软件:Windows操作系统+Visual C++。 

    三、实验要求

    1.要求采用二叉链表作为存储结构,完成哈夫曼树的创建。

    2.输出对应数据的哈夫曼编码,并求出平均编码长度。

    四、实验内容

    1.在自己的U盘的“学号+姓名”文件夹中创建“实验5”文件夹,本次实验的所有程序和数据都要求存储到本文件夹中。

    2.现在某电报公司假设有10字符进行编码,这10个字符的使用频率如下表所示,请创建哈夫曼树。

    A

    B

    C

    D

    E

    F

    G

    H

    I

    J

    19

    18

    16

    14

    12

    8

    6

    4

    2

    1

    1. 编写函数求出A~J的哈夫曼编码。
    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. struct Node {
    6. char data; // 字符
    7. int freq; // 频率
    8. Node* left;
    9. Node* right;
    10. Node(char d, int f) : data(d), freq(f), left(nullptr), right(nullptr) {}
    11. // 这里的Node(char d, int f) : data(d), freq(f), left(nullptr), right(nullptr) {}
    12. // 是一个构造函数,用于创建Node对象并进行初始化。
    13. // 它采用了成员初始化列表的方式,将传入的参数d和f分别赋值给data和freq成员变量,
    14. // 并将left和right指针初始化为nullptr(空指针)。
    15. //使用成员初始化列表的方式可以提高代码的效率和可读性。
    16. // 在构造函数体中,如果需要对成员变量进行赋值,
    17. // 需要使用赋值运算符,这会导致多余的内存分配和拷贝操作。
    18. // 而使用成员初始化列表可以直接对成员变量进行初始化,
    19. // 避免了这些额外的操作,从而提高了代码的效率。
    20. //就相当于java的构造函数
    21. };
    22. struct Compare {
    23. bool operator()(Node* a, Node* b) {
    24. return a->freq > b->freq;
    25. }
    26. };
    27. //权值越大,在树的前面越小,就是最优的路径解
    28. // 创建哈夫曼树
    29. Node* createHuffmanTree(map<char, int>& freqMap) {
    30. priority_queue, Compare> pq;//排序
    31. //这个容器相当于是队列的容器,是其中里面的,
    32. //是队列中存放的空间为容器。
    33. //插入数据的步骤,导入数据和排序
    34. for (const auto& pair : freqMap) {
    35. Node* node = new Node(pair.first, pair.second);
    36. // for (const auto& pair : freqMap) { ... } 是 C++11 引入的一种新的循环语法,
    37. // 叫做基于范围的 for 循环(range - based for loop)。
    38. //
    39. // 在这个循环中,freqMap 是你要遍历的容器,c
    40. // onst auto& pair 是每次迭代时从 freqMap 中取出的元素。
    41. //
    42. // 解释一下 const auto& pair:
    43. //
    44. // auto 是 C++11 引入的一种新的类型推断机制。
    45. // 编译器会自动推断 pair 的类型,你不需要显式地指定。
    46. // 在这个例子中,freqMap 是一个 map 类型的容器,
    47. // 所以 pair 的类型会被推断为 pair
    48. //
    49. // const 表示这个变量是常量,你不能修改它的值。
    50. // 这是一种好的编程习惯,因为它可以防止你在循环中意外地修改了 pair 的值。
    51. //
    52. // & 表示引用。如果没有& ,那么每次迭代时,
    53. // 都会从 freqMap 中复制一个元素到 pair。
    54. //如果 pair 的类型很大,那么这将会是一个很耗时的操作。
    55. // 但是有了& ,pair 就是 freqMap 中元素的一个引用,不会发生复制,可以提高代码的效率。
    56. /* new Node(pair.first, pair.second) 这部分代码调用了 Node 类的构造函数,
    57. 创建一个新的 Node 对象。这个构造函数接受两个参数:pair.first 和 pair.second。
    58. 在这个上下文中,pair 是 freqMap 中的一个元素,它是一个键值对。
    59. pair.first 是字符(char 类型),pair.second 是该字符的频率(int 类型)。*/
    60. pq.push(node);//插入
    61. }
    62. //开始构建哈夫曼树
    63. while (pq.size() > 1) {
    64. Node* left = pq.top();
    65. pq.pop();
    66. Node* right = pq.top();
    67. pq.pop();
    68. Node* parent = new Node('$', left->freq + right->freq); // 虚拟节点
    69. //这里的$只是随便找了个字符充当字符的位置,没什么特殊意义
    70. //核心是为了合成根节点
    71. parent->left = left;
    72. parent->right = right;
    73. //这两步就是为了连接后面的数据的
    74. //返回的只是根节点,但是后面节点的遍历和数据都存在left和right节点中
    75. pq.push(parent);
    76. }
    77. return pq.top();//最后size为1时,一定是根节点
    78. }
    79. // 遍历哈夫曼树获取编码
    80. void getHuffmanCodes(Node* root, string code, map<char, string>& huffmanCodes) {
    81. if (root == nullptr) {
    82. return;
    83. }
    84. if (root->left == nullptr && root->right == nullptr) { // 叶子节点
    85. huffmanCodes[root->data] = code;
    86. //获取编码关键步骤
    87. }
    88. getHuffmanCodes(root->left, code + "0", huffmanCodes);
    89. getHuffmanCodes(root->right, code + "1", huffmanCodes);
    90. }
    91. // 输出哈夫曼编码
    92. void printHuffmanCodes(map<char, string>& huffmanCodes) {
    93. for (const auto& pair : huffmanCodes) {
    94. cout << pair.first << ": " << pair.second << endl;
    95. // char string
    96. }
    97. }
    98. // 计算平均编码长度
    99. float getAverageCodeLength(map<char, int>& freqMap, map<char, string>& huffmanCodes) {
    100. float totalFreq = 0;
    101. float totalCodeLen = 0;
    102. for (const auto& pair : freqMap) {
    103. totalFreq += pair.second;
    104. totalCodeLen += pair.second * huffmanCodes[pair.first].length();
    105. //频率*单个字符的编码长度
    106. //huffmanCodes[pair.first]其实就是huffmanCodes的pair.second
    107. //只不过我们现在在按照freqMap循环.
    108. //而他们的pair.first是相同的
    109. //其实本质上来说,
    110. //freqMap用于存放频率
    111. //huffmanCodes用于存放编码
    112. }
    113. return totalCodeLen / totalFreq;
    114. }
    115. int main() {
    116. map<char, int> freqMap = {
    117. {'A', 19},
    118. {'B', 18},
    119. {'C', 16},
    120. {'D', 14},
    121. {'E', 12},
    122. {'F', 8},
    123. {'G', 6},
    124. {'H', 4},
    125. {'I', 2},
    126. {'J', 1}
    127. };//原始数据
    128. Node* root = createHuffmanTree(freqMap);
    129. //哈夫曼树的根节点,里面包括整个哈夫曼树
    130. map<char, string> huffmanCodes;//这个是用来获取哈夫曼树编码的哈希表
    131. getHuffmanCodes(root, "", huffmanCodes);
    132. cout << "Huffman Codes:" << endl;
    133. printHuffmanCodes(huffmanCodes);
    134. float averageCodeLength = getAverageCodeLength(freqMap, huffmanCodes);
    135. cout << "Average Code Length: " << averageCodeLength << endl;
    136. return 0;
    137. }

  • 相关阅读:
    设计链表复习
    希望所有计算机专业学生都知道这门课
    rust编程语言(chapter 1)
    Nginx加载Lua脚本lua_shared_dict缓存
    《昇思25天学习打卡营第25天 | 昇思MindSporeResNet50迁移学习》
    SpringBoot配置kafka
    网络安全https
    【数据结构】栈(stack)
    MyBatis核心配置文件之typeAliases
    Java之触发打印机打印
  • 原文地址:https://blog.csdn.net/ASBSIHD/article/details/134064051