哈夫曼树又称最优树,是一类带权路径长度最短的树。其中带权路径长度WPL最小的二叉树称作最优二叉树或哈夫曼树。

哈夫曼的构造算法:

(1):根据给定的n个权值{w1,w2,w3.....},构造n课只有根节点的二叉树,这n棵二叉树构成一个森林F

(2):在森林F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为其左,右子树上根节点的权值之和

(3):在森林F中删除这两棵树,同时又将新得到的二叉树加入F中。

(4):重复(2)和(3),直到F只含一棵树为止,这棵树便是哈夫曼树。

哈夫曼算法的实现:

//------哈夫曼树的存储表示-------
typedef struct
{unsigned int weight;  //用来存储各个结点的权值 unsigned int parent,LChild,RChild;  //指向双亲、孩子结点的指针
} HTNode, *HuffmanTree;  //动态分配数组,存储哈夫曼树 typedef char *HuffmanCode;  //动态分配数组,存储哈夫曼树

在构造哈夫曼树之前,还需要写出求最小权值的函数,代码如下:

///选择两个parent为0,且weight最小的结点s1和s2
void Select(HuffmanTree *ht,int n,int *s1,int *s2)
{int i,min;for(i=1; i<=n; i++){if((*ht)[i].parent==0){min=i;break;}}for(i=1; i<=n; i++){if((*ht)[i].parent==0){if((*ht)[i].weight<(*ht)[min].weight)min=i;}}*s1=min;for(i=1; i<=n; i++){if((*ht)[i].parent==0 && i!=(*s1))//除去刚才求得权值最小的,求下一个权值最小的 {min=i;break;}}for(i=1; i<=n; i++){if((*ht)[i].parent==0 && i!=(*s1)){if((*ht)[i].weight<(*ht)[min].weight) min=i;}}*s2=min;
}

算法,构造哈夫曼树:

///构造哈夫曼树ht,w存放已知n个权值
void CrtHuffmanTree(HuffmanTree *ht,int *w,int n)
{int m,i,s1,s2;m=2*n-1;    //总共的结点数 *ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//动态申请空间 for(i=1; i<=n; i++)  //1-n号存放叶子结点,初始化 {(*ht)[i].weight=w[i];(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}for(i=n+1; i<=m; i++)   //非叶子结点的初始化{(*ht)[i].weight=0;(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;} printf("哈夫曼树为: \n"); for(i=n+1; i<=m; i++)   //创建非叶子结点,建哈夫曼树{ /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2*/Select(ht,i-1,&s1,&s2);(*ht)[s1].parent=i;//定义权值最小的父结点 (*ht)[s2].parent=i;//同上 (*ht)[i].LChild=s1;(*ht)[i].RChild=s2;//根据权值的大小来对结点i的左右孩子命名 (*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight;//新生成的结点的权值为上面所求两个权值最小之和 printf("%d (%d, %d)\n",(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);}printf("\n");
} 

哈夫曼编码:

求哈夫曼编码的主要思想是:依次以叶子为出发点,向上回溯至根节点为止,回溯时走左分支则生成代码0,走右分支则生成代码1.

代码如下:

//哈夫曼编码
//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)
{char *cd;   //定义的存放编码的空间int a[100];int i,start,p,w=0;unsigned int c;hc=(HuffmanCode *)malloc((n+1)*sizeof(char *));  //分配n个编码的头指针cd=(char *)malloc(n*sizeof(char));  //分配求当前编码的工作空间cd[n-1]='\0';  //从右向左逐位存放编码,首先存放编码结束符for(i=1; i<=n; i++)  //求n个叶子结点对应的哈夫曼编码{a[i]=0;start=n-1;  //起始指针位置在最右边for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent)  //从叶子到根结点求编码{if( (*ht)[p].LChild==c){cd[--start]='1';  //左分支标1a[i]++;}else {cd[--start]='0';  //右分支标0a[i]++;}}hc[i]=(char *)malloc((n-start)*sizeof(char));  //为第i个编码分配空间strcpy(hc[i],&cd[start]);    //将cd复制编码到hc}free(cd);for(i=1; i<=n; i++)printf(" 权值为%d的哈夫曼编码为:%s\n",(*ht)[i].weight,hc[i]);for(i=1; i<=n; i++)w+=(*ht)[i].weight*a[i];printf(" 带权路径为:%d\n",w);
}

完整代码:

/*示例
****哈夫曼编码****
请输入结点个数:8
输入这8个元素的权值(均为整形):
1:27
2:4
3:87
4:21
5:2
6:21
7:1
8:25*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct
{unsigned int weight;  //用来存储各个结点的权值 unsigned int parent,LChild,RChild;  //指向双亲、孩子结点的指针
} HTNode, *HuffmanTree;  //动态分配数组,存储哈夫曼树 typedef char *HuffmanCode;  //动态分配数组,存储哈夫曼树///选择两个parent为0,且weight最小的结点s1和s2
void Select(HuffmanTree *ht,int n,int *s1,int *s2)
{int i,min;for(i=1; i<=n; i++){if((*ht)[i].parent==0){min=i;break;}}for(i=1; i<=n; i++){if((*ht)[i].parent==0){if((*ht)[i].weight<(*ht)[min].weight)min=i;}}*s1=min;for(i=1; i<=n; i++){if((*ht)[i].parent==0 && i!=(*s1))//除去刚才求得权值最小的,求下一个权值最小的 {min=i;break;}}for(i=1; i<=n; i++){if((*ht)[i].parent==0 && i!=(*s1)){if((*ht)[i].weight<(*ht)[min].weight) min=i;}}*s2=min;
}///构造哈夫曼树ht,w存放已知n个权值
void CrtHuffmanTree(HuffmanTree *ht,int *w,int n)
{int m,i,s1,s2;m=2*n-1;    //总共的结点数 *ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//动态申请空间 for(i=1; i<=n; i++)  //1-n号存放叶子结点,初始化 {(*ht)[i].weight=w[i];(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}for(i=n+1; i<=m; i++)   //非叶子结点的初始化{(*ht)[i].weight=0;(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;} printf("哈夫曼树为: \n"); for(i=n+1; i<=m; i++)   //创建非叶子结点,建哈夫曼树{ /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2*/Select(ht,i-1,&s1,&s2);(*ht)[s1].parent=i;//定义权值最小的父结点 (*ht)[s2].parent=i;//同上 (*ht)[i].LChild=s1;(*ht)[i].RChild=s2;//根据权值的大小来对结点i的左右孩子命名 (*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight;//新生成的结点的权值为上面所求两个权值最小之和 printf("%d (%d, %d)\n",(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);}printf("\n");
} //哈夫曼编码
//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)
{char *cd;   //定义的存放编码的空间int a[100];int i,start,p,w=0;unsigned int c;hc=(HuffmanCode *)malloc((n+1)*sizeof(char *));  //分配n个编码的头指针cd=(char *)malloc(n*sizeof(char));  //分配求当前编码的工作空间cd[n-1]='\0';  //从右向左逐位存放编码,首先存放编码结束符for(i=1; i<=n; i++)  //求n个叶子结点对应的哈夫曼编码{a[i]=0;start=n-1;  //起始指针位置在最右边for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent)  //从叶子到根结点求编码{if( (*ht)[p].LChild==c){cd[--start]='1';  //左分支标1a[i]++;}else {cd[--start]='0';  //右分支标0a[i]++;}}hc[i]=(char *)malloc((n-start)*sizeof(char));  //为第i个编码分配空间strcpy(hc[i],&cd[start]);    //将cd复制编码到hc}free(cd);for(i=1; i<=n; i++)printf(" 权值为%d的哈夫曼编码为:%s\n",(*ht)[i].weight,hc[i]);for(i=1; i<=n; i++)w+=(*ht)[i].weight*a[i];printf(" 带权路径为:%d\n",w);
}
int main()
{HuffmanTree HT;HuffmanCode HC;int *w,i,n,wei;printf("**哈夫曼编码**\n" );printf("请输数据组数:" );scanf("%d",&n);w=(int *)malloc((n+1)*sizeof(int)); printf("\n输入这%d个元素的权值:\n",n); for(i=1; i<=n; i++){ printf("%d: ",i); fflush(stdin);//当输入数据过多时,缓存数据 scanf("%d",&wei);//wei[]用来存放输入的权值 w[i]=wei;}CrtHuffmanTree(&HT,w,n);CrtHuffmanCode(&HT,&HC,n);system("pause");return 0;
}

测试结果为:

**哈夫曼编码**
请输数据组数:8输入这8个元素的权值:
1: 27
2: 4
3: 87
4: 21
5: 2
6: 21
7: 1
8: 25
哈夫曼树为:
3 (1, 2)
7 (3, 4)
28 (7, 21)
46 (21, 25)
55 (27, 28)
101 (46, 55)
188 (87, 101)权值为27的哈夫曼编码为:001权值为4的哈夫曼编码为:00010权值为87的哈夫曼编码为:1权值为21的哈夫曼编码为:0000权值为2的哈夫曼编码为:000110权值为21的哈夫曼编码为:011权值为1的哈夫曼编码为:000111权值为25的哈夫曼编码为:010带权路径为:428

哈夫曼算法以及求哈夫曼编码相关推荐

  1. 霍夫曼算法,构造霍夫曼树 (C++)

    //霍夫曼算法,构造霍夫曼树 #include <iostream> using namespace std; #define MAXSIZE 16 struct HaffNode {   ...

  2. C++基于优先队列建立链式哈夫曼树并求哈夫曼编码及WPL

    优先队列指针类型自定义排序方法: struct cmp { // 使用仿函数(函数对象)作为Compare双参判断式bool operator()(const HTree &node1, co ...

  3. 哈夫曼算法证明+哈夫曼编码译码程序实现

    哈夫曼算法证明 哈夫曼算法是一种贪心算法,我们考虑证明其最优子结构和贪心选择性质: 最优子结构:假设一个树是哈夫曼树,则以其任意节点为根节点的最大子树也是哈夫曼树. 证明:子树的根节点的值是其所有叶子 ...

  4. 哈夫曼算法——C/C++

    哈夫曼 1.简介 1.1 哈夫曼树定义 ​ 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树.所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根 ...

  5. 哈夫曼树及求其WPL的算法

    哈夫曼树及求其WPL算法 一.概念 给定 N N N个权值作为 N N N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree ...

  6. 哈夫曼编码压缩率计算_程序员的算法课(8)-贪心算法:理解霍夫曼编码

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/ ...

  7. 程序员的算法课(8)-贪心算法:理解霍夫曼编码

    一.一种很贪婪的算法定义 贪心是人类自带的能力,贪心算法是在贪心决策上进行统筹规划的统称. [百度百科]贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体 ...

  8. 修理牧场( 哈夫曼算法 ,贪心 )

    描述: 农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li个长度单位,于是他购买了一条很长的.能锯成N块的木头,即该木头的长度是Li 的总和. 但是农夫自己没有锯子,请人 ...

  9. 给定结点权值,求哈夫曼树的带权路径长度和

    1.哈夫曼树概念 一棵树中,从任意一个结点到达另一个结点的通路叫做路径,该路径包含的边的个数称为路径长度,每个结点带有的表示某种意义的值成为权值.从根结点到叶子结点的路径长度乘以叶子节点权值,得到的值 ...

最新文章

  1. Xilinx ISE 开发过程中生成的各种文件(二)
  2. Oracle 日志原理剖析
  3. getDimension,getDimensionPixelOffset和getDimensionPixelSize的一点说明
  4. 第一次失效_神兵小将:净化之力失效地魔兵兽,全靠特殊办法,铁心方式真霸气...
  5. 10 道关于 Java 泛型的面试题
  6. 为什么每个程序员都应该学习C语言?
  7. import java.io后报错_【JAVA小白】 问关于做IO流作业的时候出错了,错误FileOutputStream.writeBytes...
  8. 16种常见的竞品分析方法,建议收藏!
  9. 计算机无法访问文件怎么办,电脑通过局域网共享互传文件出现无法访问,怎么处理...
  10. LeetCode上的各种股票最大收益
  11. 米兔机器人恐龙拼图手册_米兔积木机器人(多形态组合、自平衡系统、App联动)...
  12. java基于ssm的高速公路收费管理系统
  13. 有一个已排好序的数组,要求输入一个数后,按原来排序规律将他插入数组。
  14. 【温故而知新】JavaWEB回顾(八)
  15. 【初赛】初赛提纲 错题本(to be countinue)
  16. cmd命令行切换目录
  17. AS3版本Progressive FLV播放方式
  18. 如何解决端口被占用的问题
  19. 技巧】利用chromg浏览器自动翻译外文网页的设置方法---电脑版
  20. 上海交通大学计算机学院录取分数线,上海交通大学

热门文章

  1. 如何用python抓取文献_浅谈Python爬虫技术的网页数据抓取与分析
  2. Cisco Packet Tracer 6.0下载安装及汉化包使用方法
  3. JAVA上传文件图片到服务器保存
  4. Libvirt网络管理
  5. Yii2友好处理404
  6. app inventor入门详细教程(音乐播放器)01
  7. 如何在一台计算机上使用两个网络,电脑一拖二显示器怎么用?一台主机两个显示器的连接设置方法...
  8. 关于println()和print()的区别
  9. 循环遍历语句(for in与for of)
  10. 使用 vue-i18n 进行Vue国际化处理,使项目切换中英文