当前位置:   article > 正文

贪心算法2——哈夫曼编码_哈夫曼编码贪心算法

哈夫曼编码贪心算法

【构造哈夫曼树】

假设有n个叶子结点,对应的权值分别是w1、w2、....,wn则哈夫曼树的构造如下:

(1)将w1,w2,....wn看成是有n课树的森林(每棵树仅有一个结点)。
(2)在森林中选出两个根结点权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为左、右子树结点权值之和。
(3)从森林中删除选取的两棵树,并将新树加入森林。
(4)重复执行(2)和(3),直到森林中只剩一棵树为止,该树即为所求的哈夫曼树。

【哈夫曼编码】


哈夫曼编码常应用在数据通信中,在数据传送时,需要将字符转换为二进制的字符串。例如,如果传送的电文是ACBAADCB,电文中有A、B、C和D四种字符,如果规定A、B、C和D的编码分别为00、01、 10和11,则上面的电文代码为00001011001,共16个二进制数。

在传送电文时,希望电文的代码尽可能的短。如果按照每个字符长度不等进行编码,出现频率高的字符采用尽可能短的编码,那么电文的代码长度就会减少。这可以利用哈夫曼树对电文进行编码,最后得到的编码就是长度最短的编码。具体构造方法如下:

假设需要编码的字符集合为{c1,c2,...,cn}相应的字符在电文中的出现次数{w1,w2,...,wn},以字符c1,c2,...,cn作为叶子结点,以w1,w2,...,wn为对应叶子结点的权值构造一棵二叉树,规定哈夫曼树的左孩子分支为0,右孩子分支为1,从根结点到每个叶子结点经过的分支组成的0和1序列就是结点对应的编码。

例如,字符集合为{A.B.C,D},各个字符相应的出现次数为{4,1,1,2},将这些字符作为叶子结点,出现次数作为叶子结点的权值,相应的哈夫曼树如图.

从图中不难看出,字符A的编码为0,字符B的编码为110,字符C的编码为11,字符D的编码为10。因此,可以得到电文ACBAADCB的C的编码为,' 01111100010111110。这样就保证了电文的编码长度最短。

在设计不等长编码时,必须使任何一个字符的编码都不是另外一个字符编码的前缀。例如,字符A的编码为11,字符B的编码为110,则字符A的编码就称为字符B的编码的前缀。如果一个代码为 11010, 在进行译码时,无法确定是将前两位译为A,还是要将前三位译为B。但是在利用哈夫曼树进行编码时,不会出现-个字符的编码是另一 个字 符编码的前缀编码。

【分析】

构造哈夫曼树的过程利用了贪心选择的性质,每次都是从结点集合中选择权值最小的两个结点构造一个新树。 这就保证了贪心选择的局部最优的性质。

code:
 

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. #include <iostream>
  5. typedef struct
  6. {
  7. unsigned int weight; /*权值*/
  8. unsigned int parent, LChild, RChild; /*指向双亲、左右孩子结点的指针*/
  9. } HTNode, *HuffmanTree; /*存储哈夫曼树*/
  10. typedef char *HuffmanCode; /*存储哈夫曼编码*/
  11. void CreateHuffmanTree(HuffmanTree *ht, int *w, int n);
  12. void Select(HuffmanTree *ht, int n, int *s1, int *s2);
  13. void CreateHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n);
  14. void main()
  15. {
  16. HuffmanTree HT;
  17. HuffmanCode HC;
  18. int *w, i, n, w1;
  19. printf("***********哈夫曼编码***********\n");
  20. printf("请输入结点个数:");
  21. scanf("%d", &n);
  22. w = (int *)malloc((n + 1)*sizeof(int));
  23. printf("输入这%d个元素的权值:\n", n);
  24. for (i = 1; i <= n; i++)
  25. {
  26. printf("%d: ", i);
  27. // fflush(stdin);
  28. scanf("%d", &w1);
  29. w[i] = w1;
  30. }
  31. CreateHuffmanTree(&HT, w, n);/*构造哈夫曼树*/
  32. CreateHuffmanCode(&HT, &HC, n);/*构造哈夫曼编码*/
  33. system("pause");
  34. }
  35. void CreateHuffmanTree(HuffmanTree *ht, int *w, int n)
  36. /*构造哈夫曼树ht,w存放已知的n个权值*/
  37. {
  38. int m, i, s1, s2;
  39. m = 2 * n - 1; /*结点总数*/
  40. *ht = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
  41. for (i = 1; i <= n; i++) /*初始化叶子结点*/
  42. {
  43. (*ht)[i].weight = w[i];
  44. (*ht)[i].LChild = 0;
  45. (*ht)[i].parent = 0;
  46. (*ht)[i].RChild = 0;
  47. }
  48. for (i = n + 1; i <= m; i++) /*初始化非叶子结点*/
  49. {
  50. (*ht)[i].weight = 0;
  51. (*ht)[i].LChild = 0;
  52. (*ht)[i].parent = 0;
  53. (*ht)[i].RChild = 0;
  54. }
  55. printf("\n哈夫曼树为: \n");
  56. for (i = n + 1; i <= m; i++) /*创建非叶子结点,建哈夫曼树*/
  57. /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个最小的结点*/
  58. {
  59. Select(ht, i - 1, &s1, &s2);
  60. (*ht)[s1].parent = i;
  61. (*ht)[s2].parent = i;
  62. (*ht)[i].LChild = s1;
  63. (*ht)[i].RChild = s2;
  64. (*ht)[i].weight = (*ht)[s1].weight + (*ht)[s2].weight;
  65. printf("%d (%d, %d)\n",
  66. (*ht)[i].weight, (*ht)[s1].weight, (*ht)[s2].weight);
  67. }
  68. printf("\n");
  69. }
  70. void CreateHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)
  71. /*从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码*/
  72. {
  73. char *cd; /*定义的存放编码的空间*/
  74. int a[100];
  75. int i, start, p, w = 0;
  76. unsigned int c;
  77. hc = (HuffmanCode *)malloc((n + 1)*sizeof(char *)); /*分配n个编码的头指针*/
  78. cd = (char *)malloc(n*sizeof(char)); /*分配求当前编码的工作空间*/
  79. cd[n - 1] = '\0'; /*从右向左逐位存放编码,首先存放编码结束符*/
  80. for (i = 1; i <= n; i++)
  81. /*求n个叶子结点对应的哈夫曼编码*/
  82. {
  83. a[i] = 0;
  84. start = n - 1; /*起始指针位置在最右边*/
  85. for (c = i, p = (*ht)[i].parent; p != 0; c = p, p = (*ht)[p].parent)
  86. /*从叶子到根结点求编码*/
  87. {
  88. if ((*ht)[p].LChild == c)
  89. {
  90. cd[--start] = '0'; /*左分支记作0*/
  91. a[i]++;
  92. }
  93. else
  94. {
  95. cd[--start] = '1'; /*右分支记作1*/
  96. a[i]++;
  97. }
  98. }
  99. /*为第i个编码分配空间*/
  100. hc[i] = (char *)malloc((n - start)*sizeof(char));
  101. strcpy(hc[i], &cd[start]); /*将cd复制编码到hc*/
  102. }
  103. free(cd);
  104. for (i = 1; i <= n; i++)
  105. printf("权值为%d的哈夫曼编码为:%s\n", (*ht)[i].weight, hc[i]);
  106. for (i = 1; i <= n; i++)
  107. w += (*ht)[i].weight*a[i];
  108. printf("带权路径为:%d\n", w);
  109. }
  110. void Select(HuffmanTree *ht, int n, int *s1, int *s2)
  111. /*选择两个parent为0,且weight最小的结点s1和s2*/
  112. {
  113. int i, min;
  114. for (i = 1; i <= n; i++)
  115. {
  116. if ((*ht)[i].parent == 0)
  117. {
  118. min = i;
  119. break;
  120. }
  121. }
  122. for (i = 1; i <= n; i++)
  123. {
  124. if ((*ht)[i].parent == 0)
  125. {
  126. if ((*ht)[i].weight < (*ht)[min].weight)
  127. min = i;
  128. }
  129. }
  130. *s1 = min;
  131. for (i = 1; i <= n; i++)
  132. {
  133. if ((*ht)[i].parent == 0 && i != (*s1))
  134. {
  135. min = i;
  136. break;
  137. }
  138. }
  139. for (i = 1; i <= n; i++)
  140. {
  141. if ((*ht)[i].parent == 0 && i != (*s1))
  142. {
  143. if ((*ht)[i].weight < (*ht)[min].weight)
  144. min = i;
  145. }
  146. }
  147. *s2 = min;
  148. }

结果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/693647
推荐阅读
相关标签
  

闽ICP备14008679号