哈夫曼编码C/C++代码实现

逃离我推掉我的手 2023-02-17 02:17 99阅读 0赞

哈夫曼编码的特点:

因为哈夫曼树的特点是:叶子结点权值越大的,离根越近。又因为构造不等长编码的原则是:字符使用频率越高,编码越短,故采用哈夫曼树进行编码可以得到最优前缀编码。
约定左分支标记为0, 右分支标记为 1

求哈夫曼编码:

为不浪费存储空间,动态分配一个长度为n(字符编码长度一定小于n) 的一维数组cd, 用来临时存放当前正在求解的第i个字符的编码,当第i个字符的编码求解完毕后,根据数组cd的字符串长度分配HC[i]的空间,然后将数组cd中的编码复制到HC[i]中。

依照上一篇文章的哈夫曼树:
在这里插入图片描述
哈夫曼编码表HC:
在这里插入图片描述
注意:由于哈夫曼树不唯一,故哈夫曼编码也不唯一。

代码如下:

  1. #include<stdio.h>
  2. #include<iostream>
  3. //哈夫曼树定义
  4. typedef struct {
  5. int weight;
  6. int parent, lchild, rchild;
  7. }HTNode, *HuffmanTree;
  8. //选择两个双亲域为0且权值最小的结点,并返回在HT中的序号s1,s2
  9. void Select(HuffmanTree &HT, int n, int &s1, int &s2)
  10. {
  11. //寻找第一个双亲域为0且权值最小的结点
  12. int min;
  13. for (int i = 1; i <= n; i++) //找到第一个双亲域为0的,下标暂存到min
  14. {
  15. if (HT[i].parent == 0)
  16. {
  17. min = i;
  18. break;
  19. }
  20. }
  21. for (int i = 1; i <= n; i++)
  22. {
  23. if (HT[i].parent == 0)
  24. {
  25. if (HT[i].weight < HT[min].weight)
  26. {
  27. min = i;
  28. }
  29. }
  30. }
  31. s1 = min;
  32. //寻找第二个双亲域为0且权值最小的结点
  33. for (int i = 1; i <= n; i++) //找到第一个双亲域为0的,下标暂存到min
  34. {
  35. if (HT[i].parent == 0 && i != s1)
  36. {
  37. min = i;
  38. break;
  39. }
  40. }
  41. for (int i = 1; i <= n; i++)
  42. {
  43. if (HT[i].parent == 0 && i != s1)
  44. {
  45. if (HT[i].weight < HT[min].weight)
  46. {
  47. min = i;
  48. }
  49. }
  50. }
  51. s2 = min;
  52. }
  53. //输出
  54. void println(HuffmanTree &HT, int m)
  55. {
  56. printf("==============================\n");
  57. for (int i = 1; i <= m; i++)
  58. {
  59. printf("%d, ", i);
  60. printf("%d ", HT[i].weight);
  61. printf("%d ", HT[i].parent);
  62. printf("%d ", HT[i].lchild);
  63. printf("%d \n", HT[i].rchild);
  64. printf("---------------------------\n");
  65. }
  66. }
  67. //创建哈夫曼树
  68. void CreateHuffmanTree(HuffmanTree &HT,int n, int *ht)
  69. {
  70. //初始化
  71. int i, m = 2 * n - 1, s1, s2; //m为所有结点的个数
  72. if (n <= 1) return;
  73. HT = new HTNode[m + 1]; //0号不用从1开始,多申请一行,前1~n存放叶子结点
  74. for (i = 1; i <= m; ++i) //遍历每一个结点并赋值为0
  75. {
  76. HT[i].parent = 0;
  77. HT[i].lchild = 0;
  78. HT[i].rchild = 0;
  79. }
  80. //创建树
  81. for (i = 1; i <= n; ++i) //把叶子结点权值放入表中
  82. {
  83. HT[i].weight = ht[i - 1];
  84. }
  85. printf("\nHT的初态\n");
  86. println(HT, m);
  87. for (int i = n + 1; i <= m; ++i) //从非叶子结点开始创建
  88. {
  89. Select(HT, i - 1, s1, s2); //选择两个最小的结点
  90. HT[s1].parent = i;
  91. HT[s2].parent = i; //把叶子结点双亲域赋上
  92. HT[i].lchild = s1;
  93. HT[i].rchild = s2;
  94. HT[i].weight = HT[s1].weight + HT[s2].weight;
  95. }
  96. printf("\nHT的终态\n");
  97. println(HT, m);
  98. }
  99. //哈夫曼编码
  100. typedef char **HuffmanCode; //哈夫曼编码表,指针数组
  101. char *cd; //用来临时存放当前正在求解的第i个字符的编码
  102. int start; //cd数组的下标指针
  103. void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
  104. {
  105. int i, c, f;
  106. HC = new char*[n + 1];
  107. cd = new char[n];
  108. cd[n - 1] = '\0';
  109. for (i = 1; i <= n; ++i)
  110. {
  111. start = n - 1;
  112. c = i; //当前结点
  113. f = HT[i].parent; //双亲结点
  114. while (f != 0) //因为cd数组的start指针从后往前,所以哈夫曼树从下往上即从叶子到根结点进行比较,
  115. {
  116. if (HT[f].lchild == c) cd[--start] = '0';
  117. else cd[--start] = '1';
  118. c = f;
  119. f = HT[f].parent;
  120. }
  121. HC[i] = new char[n - start];
  122. strcpy(HC[i], &cd[start]);
  123. //输出
  124. printf("第%d组--->", i);
  125. for (int j = start; j <= n - 1; ++j)
  126. {
  127. printf("%c ", cd[j]);
  128. }
  129. printf("\n");
  130. }
  131. delete cd;
  132. }
  133. int main() {
  134. HuffmanTree HT; //构造哈夫曼
  135. HuffmanCode HC; //哈夫曼编码
  136. int n = 8; //n为叶子节点的个数
  137. int ht[8] = {
  138. 5,29,7,8,14,23,3,11 };
  139. CreateHuffmanTree(HT, n, ht);
  140. CreatHuffmanCode(HT, HC, n);
  141. }

运行结果:

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,99人围观)

还没有评论,来说两句吧...

相关阅读

    相关 树和编码

    哈夫曼树 给定n个权值作为n的叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径

    相关 树和编码

    当树中的节点被赋予一个表示某种意义的数值,我们称之为该节点的权。从树的根节点到任意节点的路径长度(经过的边数)与该节点上权值的乘积称为该节点的带权路径长度。树中所有叶节点的带权