霍夫曼树及霍夫曼编码的C语言实现

霍夫曼树及霍夫曼编码的C语言实现从周五开始学习霍夫曼树 一直到今天终于完成 期间遇到了各种各样的棘手的问题 通过一遍遍在纸上分析每一步的具体状态得以解决 现在对学习霍夫曼树的过程加以记录首先介绍霍夫曼树霍夫曼树 HuffmanTree 又称最优二叉树 是一类带权路径长度最短的树 假设有 n 个权值 w1 w2 wn 如果构造一棵有 n 个叶子节点的二叉树 而这 n 个叶子节点的权值是 w1 w2 wn 则所构造出的带权路径长度

从周五开始学习霍夫曼树,一直到今天终于完成,期间遇到了各种各样的棘手的问题,通过一遍遍在纸上分析每一步的具体状态得以解决。现在对学习霍夫曼树的过程加以记录

首先介绍霍夫曼树

霍夫曼树(Huffman Tree),又称最优二叉树,是一类带权路径长度最短的树。假设有n个权值{w1,w2,…,wn},如果构造一棵有n个叶子节点的二叉树,而这n个叶子节点的权值是{w1,w2,…,wn},则所构造出的带权路径长度最小的二叉树就被称为赫夫曼树。

这里补充下树的带权路径长度的概念。树的带权路径长度指树中所有叶子节点到根节点的路径长度与该叶子节点权值的乘积之和,如果在一棵二叉树中共有n个叶子节点,用Wi表示第i个叶子节点的权值,Li表示第i个也叶子节点到根节点的路径长度,则该二叉树的带权路径长度 WPL=W1*L1 + W2*L2 + … Wn*Ln。

根据节点的个数以及权值的不同,赫夫曼树的形状也各不相同,赫夫曼树具有如下特性:

假如给定如下5个权值: 则按照以上步骤,可以构造出如下面左图所示的赫夫曼树,当然也可能构造出如下面右图所示的赫夫曼树,这并不是唯一的。 

霍夫曼树及霍夫曼编码的C语言实现

霍夫曼树及霍夫曼编码的C语言实现

霍夫曼树及霍夫曼编码的C语言实现

霍夫曼树及霍夫曼编码的C语言实现
即左分支编码为字符0,右分支编码为字符1,将从根节点到叶子节点的路径上分支字符组成的字符串作为叶子节点字符的编码,这便是赫夫曼编码。我们根据上面左图可以得到各叶子节点的赫夫曼编码如下:
权值为5的也自己节点的赫夫曼编码为:11
权值为4的也自己节点的赫夫曼编码为:10
权值为3的也自己节点的赫夫曼编码为:00
权值为2的也自己节点的赫夫曼编码为:011
权值为1的也自己节点的赫夫曼编码为:010





而对于上面右图,则可以得到各叶子节点的赫夫曼编码如下: 权值为5的也自己节点的赫夫曼编码为:00 权值为4的也自己节点的赫夫曼编码为:01 权值为3的也自己节点的赫夫曼编码为:10 权值为2的也自己节点的赫夫曼编码为:110 权值为1的也自己节点的赫夫曼编码为:111 

下面给出C语言实现

#include<stdio.h> #include<stdlib.h> #include<string.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status; /*定义霍夫曼树节点*/ typedef struct HTNode{ int parent;/*记录双亲*/ int Lchild;/*左右子树*/ int Rchild; int Weight;/*记录权重*/ }HTNode; typedef struct HTNode * HuffmanTree; typedef char HuffmanCode; /*在前k棵树种找到权重最小的树*/ int Min(HuffmanTree HT,int k){ int i=0,min_weight=0,min=0; /*找出第一个双亲存在的节点,将其权值赋值给min_weight*/ /*注意此处不能直接将HT[0].weight赋给min_weight,原因是如果HT[0].weight最小,那么在第一次构造二叉树时就会被选走,而后续的每一轮选择最小权值构造二叉树的比较 还是先用HT[0].weight的值来进行判断,这样又会再次将其选走,从而产生逻辑上的错误。*/ while(HT[i].parent!=-1) i++; min_weight=HT[i].Weight; min=i; for(i;i<k;i++){ if(HT[i].Weight<min_weight&&HT[i].parent==-1){ min_weight=HT[i].Weight; min=i; } } /*找到最小权重的树,将其双亲置为1*/ /*!!!!!注意这的HT的下标!!!!!一晚上才找出这个小问题,别写成HT[i]!!!!!!*/ HT[min].parent=1; return min; } /*从前k棵树中选出权重最小的两棵树,将其序号赋给min1和min2*/ Status SelectMin(HuffmanTree HT,int k,int &min1,int &min2){ min1=Min(HT,k); min2=Min(HT,k); return OK; } /*创建一课霍夫曼树,-1表示不存在*/ /*wet为一个记录权重的数组,类型为int*/ HuffmanTree CreateHuffmanTree(HuffmanTree HT,int *wet,int n){ int i=0; int total=2*n-1;/*有n个数据需要编码,即有n个叶子节点,也就有n-1个度为2的节点,总节点数为n+n-1=2*n-1*/ /*初始状态下,前n个节点的双亲,左右子树应该均为-1,权重为对应的权重*/ /*用HT的前n个分量存储n棵树(由n个待编码的数据组成)的森林*/ /*申请total个int组成的动态数组*/ HT=(HuffmanTree)malloc(total*sizeof(HTNode)); if(!HT) return ERROR; for(i=0;i<n;i++){ HT[i].Lchild=-1; HT[i].parent=-1; HT[i].Rchild=-1; HT[i].Weight=*wet; wet++; } /*对n到total的分量进行初始化*/ for(i;i<total;i++){ HT[i].Lchild=-1; HT[i].Rchild=-1; HT[i].parent=-1; HT[i].Weight=0; } /*用HT的后n-1个分量存储霍夫曼树*/ /*调用函数SelectMin找出森林中两棵权重最小的树*/ int min1=0,min2=0; for(i=n;i<total;i++){ SelectMin(HT,i,min1,min2); HT[min1].parent=i; HT[min2].parent=i; HT[i].Lchild=min1; HT[i].Rchild=min2; HT[i].Weight=HT[min1].Weight+HT[min2].Weight; } return HT; } /*从叶子节点开始逆向进行霍夫曼编码*/ /*HC用来储存霍夫曼编码值*/ Status HuffmanCoding(HuffmanTree HT,HuffmanCode &HC,int n){ /*HC本身是一个char类型数组的指针,其指向n个char类型的地址,所以给HC分配内存应该写成下面那样*/ HC=(HuffmanCode)malloc(n*sizeof(char *)); if(!HC) return ERROR; /*声明一个动态数组code,用来临时存储霍夫曼编码,数组含有n-1个霍夫曼码,加上一个'\0'终止符正好是n个元素,所以分配内存时*/ char *code; code=(char *)malloc(n*sizeof(char)); if(!code) return ERROR; code[n-1]='\0';/*让最后一个元素为终止符*/ int i=0; for(i=0;i<n;i++){ int current=i; int father=HT[i].parent; int start=n-1; while(father!=-1){ if(current==HT[father].Lchild) code[--start]='0'; else code[--start]='1'; current=father; father=HT[father].parent; } /*HC[i]用于最终存储霍夫曼码,是char类型的数组,有n个char类型的数据*/ HC[i]=(char *)malloc((n-start)*sizeof(char)); if(!HC[i]) return ERROR; /*从临时空间中复制到HC[i]中*/ strcpy(HC[i],code+start); } /*释放临时存储空间*/ free(code); code=NULL; return OK; } int main(void){ int amount=0,i=0; int *wet=(int *)malloc(amount*sizeof(int)); printf("请输入要编码的字符个数(个数为整型且>1)"); scanf("%d",&amount); while(amount<=1){ printf("字符个数必须大于1\n"); scanf("%d",&amount); } printf("请输入要编码的字符的权值"); for(i=0;i<amount;i++){ scanf("%d",wet+i); } HuffmanTree HT; HT=CreateHuffmanTree(HT,wet,amount); HuffmanCode HC; HuffmanCoding(HT,HC,amount); for(i=0;i<amount;i++){ puts(HC[i]); } free(wet); return OK; }

参考了http://blog.csdn.net/ns_code/article/details/这篇博客

问题一:出现在函数Min中

int Min(HuffmanTree HT,int k){ int min=0,min_weight=0,i=0; /*注意此处是先寻找双亲不存在的节点再赋值,给min_weight赋值应该在循环以外*/ while(HT[i].parent!=-1) i++; min_weight=HT[i].weight; min=i; for(i;i<k;i++){ if(HT[i].weight<min_weight&&HT[i].parent==-1){ min_weight=HT[i].weight; min=i; } } /*赋值之后切记要将其双亲赋值为1,否则会出现错误*/ HT[min].parent=1; return min; }

问题二:出现在CreateHuffmanTree函数中

HuffmanTree CreateHuffmanTree(HuffmanTree HT,int * wet,int amount){ int i=0,min_weight=0,min=0; int total=2*amount-1; /*注意此处分配内存时,sizeof()中应该是HTNode!!要对HT的本质进行理解,HT是一棵树!*/ HT=(HuffmanTree)malloc(total*sizeof(HTNode)); if(!HT) return ERROR; for(i=0;i<amount;i++){ 
      HT[i].Lchild=-1; HT[i].parent=-1; HT[i].Rchild=-1; HT[i].weight=*wet; wet++; } for(i;i<total;i++){ HT[i].Lchild=-1; HT[i].Rchild=-1; HT[i].parent=-1; HT[i].weight=0; } int min1=0,min2=0; /*注意此处i的初始值,要对这个过程加以理解,霍夫曼树是用amount之后的分量来存储的,故不能写i=0*/ for(i=amount;i<total;i++){ 
      SelectMin(HT,min1,min2,i); HT[min1].parent=i; HT[min2].parent=i; HT[i].Lchild=min1; HT[i].Rchild=min2; HT[i].weight=HT[min1].weight+HT[min2].weight;/*注意新生成的节点的权值为选出的两个最小的节点的权值之和*/ } return HT; }

这个问题主要是对建立霍夫曼树的过程理解不够透彻,导致在为HT分配内存时写错大小,和循环过程中将i的初始值误写为0,今后应当注意。

问题三:出现在HuffmanCoding函数中

Status HuffmanCoding(HuffmanTree HT,HuffmanCode &HC,int amount){ int i=0; /*要先给HC分配内存。HC存储了amount个指向霍夫曼码的指针*/ HC=(HuffmanCode)malloc(amount*sizeof(char *)); if(!HC) return ERROR; char * code; code=(char *)malloc(amount*sizeof(char)); if(!code) return ERROR; code[amount-1]='\0'; for(i=0;i<amount;i++){ int current=i; int start=amount-1; int father=HT[i].parent; while(father!=-1){ if(current==HT[father].Lchild) code[--start]='0'; else code[--start]='1'; current=father; father=HT[father].parent; } /*HC[i]是HC中若干个存储单元之一,存储着一个具体字符的霍夫曼码,所以分配内存空间时sizeof()中为char*/ HC[i]=(char *)malloc((amount-start)*sizeof(char)); if(!HC[i]) return ERROR; strcpy(HC[i],code+start); } free(code); code=NULL; }

对HC的用途理解不够到位,再次强调,HC是一个存储了若干个指向霍夫曼码的指针的数组,HC[i]则用于存储具体字符的霍夫曼码

问题四:反复出现在主函数之中,必须加以记录,认真反省

int main(void){ HuffmanTree HT; int amount=0; printf("请输入需要编码的字符个数"); scanf("%d",&amount); int i=0; int * wet=(int *)malloc(amount*sizeof(int)); printf("请输入每个字符的权重"); for(i=0;i<amount;i++){ scanf("%d",wet+i); } /*此处忘记写HT=,导致未能成功建立霍夫曼树,由于这里要改变函数形参的值,一般情况考虑传入指针变量,但这个函数如果写成指针又太复杂, 很容易出错,故这里使用return把建立好的霍夫曼树直接返回,会方便许多,但是切记要把返回值赋给HT!!!*/ HT=CreateHuffmanTree(HT,wet,amount); HuffmanCode HC; HuffmanCoding(HT,HC,amount); for(i=0;i<amount;i++){ puts(HC[i]); } free(wet); } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232369.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Fread函数的用法「建议收藏」

    Fread函数的用法「建议收藏」转载地址 https://www.cnblogs.com/melons/p/5791874.html 函数原型:size_t fread( void *buffer, size_t size, size_t count, FILE *stream )  buffer 是读取的数据存放的内存的指针(可以是数组,也可以是新开辟的空间,buffer就是…

    2025年11月9日
    5
  • vs2013下载及安装教程_vs 2013

    vs2013下载及安装教程_vs 2013下面是VS2013对的网盘分享链接:https://pan.baidu.com/s/1K3BRe9TwM5RT5VujeRXx1w提取码:2yd6复制这段内容后打开百度网盘手机App,操作更方便哦下面是安装步骤链接:https://pan.baidu.com/s/1DBctGcVa-Tj3IAR44L6lEA提取码:zaag复制这段内容后打开百度网盘手机App,操作更方便哦…

    2025年10月25日
    3
  • oj在计算机领域中指什么,【计算机专业论文】计算机专业教学中OJ平台的应用(共2762字)…

    oj在计算机领域中指什么,【计算机专业论文】计算机专业教学中OJ平台的应用(共2762字)…摘要:传统的教学模式对计算机专业学生的能力培养存在着诸多问题,而OJ(OnlineJudge在线检测程序源代码)平台为计算机教学提供了新的思路,因为OJ平台在学生日常训练方面有一套行之有效的机制,所以对学生的学习兴趣、分析解决问题能力、创新能力等方面的培养都起到了积极的推动作用,OJ平台还可以对学生实践能力进行最直接的考核,因此将OJ平台引入计算机专业教学,可实现以平台促教学,以平台促教改。关键词…

    2022年6月16日
    31
  • 网页中嵌入特殊字体

    网页中嵌入特殊字体

    2021年5月26日
    105
  • 1165. 单词环(spfa求负环)「建议收藏」

    1165. 单词环(spfa求负环)「建议收藏」我们有 n 个字符串,每个字符串都是由 a∼z 的小写英文字母组成的。如果字符串 A 的结尾两个字符刚好与字符串 B 的开头两个字符相匹配,那么我们称 A 与 B 能够相连(注意:A 能与 B 相连不代表 B 能与 A 相连)。我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。如下例:ababcbckjacacaahoynaab第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,

    2022年8月9日
    4
  • java softReference 详解 .[通俗易懂]

    java softReference 详解 .[通俗易懂]本文介绍对象的强、软、弱和虚引用的概念、应用。1.对象的强、软、弱和虚引用  在JDK1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

    2025年10月6日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号