哈夫曼编码/ 译码系统(树应用)

[问题描述]

任意给定一个仅由 26 个大写英文字母组成的字符序列,根据哈夫曼编码算法,求得每个字符的哈夫曼编码。

要求:

1)输入一个由 26 个英文字母组成的字符串,请给出经过哈夫曼编码后的编码序列及其编码程度。(编码)

2)采用上一问题的哈夫曼编码,给定一串编码后的序列,将其翻译为原字符序列。(解码)

样例:

输入原串:CASBCATBSATBAT

输出:

A 00

B 10

T 11

C 010

S 011

输入:0100011

输出:CAT

设计思路

自顶向下拆解:

  1. 编码部分(Encode)

    a)  输入字符串;

    b)  根据输入的字符串创建HuffmanTree;

    c)  遍历HuffmanTree 得到各结点的HuffmanCode;

    d)  收集各个叶节点的字符及其HuffmanCode,做成HuffmanCodeTable并输出。

     :由HuffmanTree的构造方法可知:叶节点 <=> “有效字符”

  2. 译码部分(Decode)

    a)  输入密文字符串;

    b)  在HuffmanCodeTable中逐字匹配以找到明文字符 / 或是直接遍历HuffmanTree即可;

    c)  输出明文字符串。

思维导图如下:

代码实现

两个类:

  1. 1 class HuffmanTNode // 用于Encode时的HuffmanTree
  2. 2 {
  3. 3 public:
  4. 4 char ch; // 字符
  5. 5 int weight; // 权值、频次
  6. 6 HuffmanTNode* lchild;
  7. 7 HuffmanTNode* rchild;
  8. 8 string HuffmanCode;
  9. 9
  10. 10 HuffmanTNode() : HuffmanCode(), lchild(nullptr), rchild(nullptr) {}
  11. 11 HuffmanTNode(char a, int wei) : ch(a), weight(wei), HuffmanCode(), lchild(nullptr), rchild(nullptr) {}
  12. 12 HuffmanTNode(const HuffmanTNode &other) : ch(other.ch), weight(other.weight), HuffmanCode(other.HuffmanCode),
  13. 13 lchild(other.lchild), rchild(other.rchild) {}
  14. 14 ~HuffmanTNode() {
  15. 15 // 对左右子树的递归清理
  16. 16 if (lchild != nullptr) {
  17. 17 delete lchild;
  18. 18 }
  19. 19 lchild = nullptr;
  20. 20 if (rchild != nullptr) {
  21. 21 delete rchild;
  22. 22 }
  23. 23 rchild = nullptr;
  24. 24 }
  25. 25 };
  26. 26
  27. 27 class HuffmanCharAndCode // 用于Decode时的查找表
  28. 28 {
  29. 29 public:
  30. 30 char ch; // 字符
  31. 31 string HuffmanCode; // 哈夫曼编码
  32. 32
  33. 33 HuffmanCharAndCode() {}
  34. 34 HuffmanCharAndCode(char a, const string &code) : ch(a){
  35. 35 HuffmanCode += code;
  36. 36 }
  37. 37 ~HuffmanCharAndCode() {}
  38. 38 };

包装类 * 2

Encode:

  1. main() 函数展示了程序的主干,如下:

  1. 1 int main() {
  2. 2
  3. 3 /*************************ENCODE*************************/
  4. 4 // 1 输入字符串
  5. 5 printf("ENCODE: \n");
  6. 6 string str;
  7. 7 printf("plz Enter String :");
  8. 8 std::cin >> str;
  9. 9
  10. 10 // 2 根据输入的字符串创建HuffmanTree
  11. 11 HuffmanTNode* root = CreateHuffmanTree(str);
  12. 12 while (root == nullptr) { // 非法输入字符串的处理
  13. 13 printf("\nERROR! Invalid Input!\n");
  14. 14 printf("plz Enter String Again :");
  15. 15 std::cin >> str;
  16. 16 root = CreateHuffmanTree(str);
  17. 17 }
  18. 18
  19. 19 // 3 根据生成的HuffmanTree得到HuffmanCodeTable
  20. 20 vector<char> vec_code;
  21. 21 GetHuffmanCode(root, vec_code);
  22. 22 vector<HuffmanCharAndCode*> HuffmanCodeTable;
  23. 23 GetHuffmanCodeTable(root, HuffmanCodeTable);
  24. 24
  25. 25 // 4 输出字符及其对应的HuffmanCode
  26. 26 printf("\nSUCCESS! the HuffmanCode is listed as below: \n");
  27. 27 std::sort(HuffmanCodeTable.begin(), HuffmanCodeTable.end(), compare_CodeTable);
  28. 28 OutputHuffmanCode(HuffmanCodeTable);
  29. 29
  30. 30 /*************************DECODE*************************/
  31. 31
  32. 32 // 1 输入密文字符串
  33. 33 printf("\nDECODE: \n");
  34. 34 string ciphertext;
  35. 35 printf("plz Enter CipherText :");
  36. 36 std::cin >> ciphertext;
  37. 37
  38. 38 // 2 解码密文字符串得到明文
  39. 39 string cleartext;
  40. 40 int error_index = 1;
  41. 41 while (HuffmanDecode(ciphertext, HuffmanCodeTable, cleartext, error_index) == false) { // 非法密文字符串的处理
  42. 42 printf("\nERROR! Invalid Input Ciphertext!\n");
  43. 43 printf("\nthe POTENTIAL index of error char in at: %d\n", error_index);
  44. 44 printf("plz Enter CipherText Again :");
  45. 45 std::cin >> ciphertext;
  46. 46 }
  47. 47
  48. 48 // 3 输出明文字符串
  49. 49 printf("SUCCESS! the ClearText is: \n");
  50. 50 std::cout << cleartext << std::endl;
  51. 51
  52. 52 // 释放HuffmanTable
  53. 53 for (HuffmanCharAndCode* elem : HuffmanCodeTable) {
  54. 54 delete elem;
  55. 55 }
  56. 56
  57. 57 system("pause");
  58. 58 return 0;
  59. 59 }

main()

  2. 创建HuffmanTree(),如下:

  1. 1 HuffmanTNode* CreateHuffmanTree(const string &str) {
  2. 2 // 字符串检查
  3. 3 if (str.size() == 0) { return nullptr; }
  4. 4 // 字符频次记录桶
  5. 5 vector<HuffmanTNode*> bucket;
  6. 6 // 填充字符频次记录桶
  7. 7 if (FillCharFrequencyBucket(str, bucket) == false) { return nullptr; }
  8. 8 // 若只有一个字符 那么没必要用哈夫曼编码
  9. 9 if (bucket.size() == 1) { return nullptr; }
  10. 10
  11. 11
  12. 12 // 当bucket中只剩下一个元素,那么它就是HuffmanTree的root
  13. 13 while (bucket.size() != 1) {
  14. 14 // 令bucket中元素按weight非递增排序
  15. 15 std::sort(bucket.begin(), bucket.end(), compare_bucket);
  16. 16
  17. 17 // 从bucket中取两个最小的elem
  18. 18 HuffmanTNode *elem_1 = bucket[bucket.size() - 1]; bucket.pop_back();
  19. 19 HuffmanTNode *elem_2 = bucket[bucket.size() - 1]; bucket.pop_back();
  20. 20 // 根据 elem1 与 elem2 的weight 得到它俩的 root
  21. 21 HuffmanTNode *root_tmp = new HuffmanTNode('\0', elem_1->weight + elem_2->weight);
  22. 22 // root_tmp, elem_1, elem_2 三者组成三结点的二叉树
  23. 23 root_tmp->lchild = elem_2;
  24. 24 root_tmp->rchild = elem_1;
  25. 25
  26. 26 // root_tmp 入桶
  27. 27 bucket.push_back(root_tmp);
  28. 28 }
  29. 29
  30. 30 // bucket中最后元素,就是哈夫曼树的根节点指针
  31. 31 HuffmanTNode *root = bucket[0];
  32. 32 return root;
  33. 33 }

CreateHuffmanTree()

  3. 遍历得到各结点HuffmanCode,再收集各个叶节点的Code即可,我就只放前者吧,稍微有趣一点,如下:

  1. 1 // 将vec_code中的字符转为字符串 并赋给当前节点curr->HuffmanCode
  2. 2 void visit(HuffmanTNode *curr, const vector<char> &vec_code) {
  3. 3 string Code(vec_code.begin(), vec_code.end());
  4. 4 curr->HuffmanCode = Code;
  5. 5 }
  6. 6
  7. 7 // (前序遍历改) 得到HuffmanTree上每个结点的HuffmanCode
  8. 8 void GetHuffmanCode(HuffmanTNode *root, vector<char> vec_code) {
  9. 9 if (root != nullptr) {
  10. 10 visit(root, vec_code);
  11. 11 vec_code.push_back('0');
  12. 12 GetHuffmanCode(root->lchild, vec_code);
  13. 13 vec_code.pop_back();
  14. 14 vec_code.push_back('1');
  15. 15 GetHuffmanCode(root->rchild, vec_code);
  16. 16 vec_code.pop_back();
  17. 17 }
  18. 18 }

先序遍历改,得各结点的HuffmanCode

Decode:

  好像不是很难,就不放了,照着思路往下做就行。(主要我写的不是很好看...)

运行结果

ps: HuffmanCode不是唯一的,是根据你生成的HuffmanTree 和 你定义的Code规则而定,比如说我的Code规则是左0右1。

行,那就先这样吧,这代码写了好久都有些遗忘了,幸好之前做了思维导图,希望能帮助到你。

数据结构实验代码分享 - 3 (哈夫曼树 / HuffmanTree)的更多相关文章

  1. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  2. Java数据结构(十二)—— 霍夫曼树及霍夫曼编码

    霍夫曼树 基本介绍和创建 基本介绍 又称哈夫曼树,赫夫曼树 给定n个权值作为n个叶子节点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称为最优二叉树 霍夫曼树是带权路径长度最短的树,权值较 ...

  3. 数据结构之C语言实现哈夫曼树

    1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径. 从 ...

  4. 数据结构-二叉树(6)哈夫曼树(Huffman树)/最优二叉树

    树的路径长度是从树根到每一个结点的路径长度(经过的边数)之和. n个结点的一般二叉树,为完全二叉树时取最小路径长度PL=0+1+1+2+2+2+2+… 带权路径长度=根结点到任意结点的路径长度*该结点 ...

  5. (哈夫曼树)HuffmanTree的java实现

    参考自:http://blog.csdn.net/jdhanhua/article/details/6621026 哈夫曼树 哈夫曼树(霍夫曼树)又称为最优树. 1.路径和路径长度在一棵树中,从一个结 ...

  6. javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题

    赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支 ...

  7. 哈夫曼树(三)之 Java详解

    前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:htt ...

  8. 哈夫曼树(二)之 C++详解

    上一章介绍了哈夫曼树的基本概念,并通过C语言实现了哈夫曼树.本章是哈夫曼树的C++实现. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载 ...

  9. 哈夫曼树(一)之 C语言详解

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  10. poj3253 Fence Repair【哈夫曼树+优先队列】

    Description Farmer John wants to repair a small length of the fence around the pasture. He measures ...

随机推荐

  1. iview Button按钮 全局click事件vue拦截 节流 - 防抖 Throttle debounce

    这里是按钮的节流,就没用防抖 const setVueClickGlobalThrottle = Vue => { // 节流 const on = Vue.prototype.$on Vue. ...

  2. linux-关于conio.h文件的文件缺失问题

    链接: https://pan.baidu.com/s/1Qzo4CkJB1_5E-3rDLtfG4Q 提取码: fh65 编辑以下这个依赖库就可以了 $ cd libconio-1.0.0 $ ./ ...

  3. day26--Java集合09

    Java集合09 18.TreeSet 元素无序:插入顺序和输出顺序不一致 可以按照一定的规则进行排序,具体排序方式取决于构造方法: TreeSet () :根据其元素的自然排序进行排序 TreeSe ...

  4. 文旅新体验!3DCAT助力广州非遗“元宇宙”街区炫酷亮相

    2022年6月12日,2022年"文化和自然遗产日"广州非遗宣传展示主会场暨广州非遗街区(北京路)开街仪式在南越王博物院(王宫展区)举行. 本次活动由广州市文化广电旅游局主办,广州 ...

  5. 专访冠军考拉ok|“新人问我学Blender能找到工作吗,我回复不能”

    "新锐先锋,玩转未来"--首届实时染3D动画创作大赛由瑞云科技主办,英伟达.青椒云.3DCAT实时渲染云协办,戴尔科技集团.Reallusion.英迈.万生华态.D5渲染器.中视典 ...

  6. Python基于Excel生成矢量图层及属性表信息:ArcPy

      本文介绍基于Python中ArcPy模块,读取Excel表格数据并生成带有属性表的矢量要素图层,同时配置该图层的坐标系的方法. 1 任务需求   首先,我们来明确一下本文所需实现的需求.   现有 ...

  7. 构建个人博客网站(基于Python Flask)

    本文由 Ficow Shen 首发于 Ficow Shen's Blog. 文章概览 前言 Sketch HTML, CSS, JavaScript Python & Flask & ...

  8. FFmpeg开发笔记(五)更新MSYS的密钥环

    ​ <FFmpeg开发实战:从零基础到短视频上线>一书提到:使用MSYS对FFmpeg进行交叉编译时,需要事先安装交叉编译工具链,也就是执行下面命令. pacman -S mingw-w6 ...

  9. KingbaseES V8R6集群案例---一主二备架构单个备库宕机事务影响测试

    KingbaseES V8R6集群案例---一主二备架构单个备库宕机事务影响测试 案例说明: 对于KingbaseES V8R6集群,在sync模式下,对于一主一备架构,如果备库宕机时,主库事务com ...

  10. KingbaseES V8R3 集群运维案例 --操作系统‘soft lockup’引起的failover切换

    案例说明: 在国产中标麒麟系统生产环境中,监控发现KingbaseES V8R3集群发生了failover的主备切换,客户需要给出分析报告,说明此次集群发生failover切换的原因,本次文档通过分析 ...