刻意练习-理解哈夫曼树构建哈夫曼表C语言
理解哈夫曼树构建哈夫曼表
一、哈夫曼树的作用
哈夫曼树是一个二叉树,是可以将一些字节重新编码 ,而且能够使用最少的空间。所以也叫最优二叉树。
比如这段字符串
damainnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnaaaaaaaaaaaaaaaaaaaaaaaaaayikunnnnnnnnnnnnnnnaaaaaaaaaa
这是100个字节。但是如果你构造一个表如下:
haffuman_map haffuman_table[]= {{'n',0b0},{'a',0b10},{'d',0b11100},{'i',0b1101},{'y',0b1111},{'u',0b11101},{'m',0b11001},{'k',0b11000}, };
看出,n只需要一个bit就可以,a只需要两个bit。
使用这个表压缩这段字符串之后有多大呢?
压缩之后只有25个字节了。
具体如何压缩,看这里。
二、如何构造哈夫曼树
我也没想到什么好的表达,就把大话数据结构中的描述搬运一下。
简单的说,就是不断的找表中权值最小的两个节点,取出来放在最下面,然后把两个节点的权值相加,再放到表中。然后再次排序,取出最小的两个节点。以此类推。直到只有一个节点了,就是根节点。
三、一些问题?
什么是权值?
在例子中,就是这个字符出现的频率。频数。出现的越多,权值越大。
这里是100个字节,'n’字符出现了55次,权就是55。
有了哈夫曼树,如何获得编码呢?
将路径左边定义为0,右边定义为1。然后走过的路径就是编码方式。图中D的编码方式就是00,A的编码方式就是01。
四、抽象
解决问题要分两步,第一步是构建haffuman树。会用到排序算法。我是搬运了我之前写的希尔排序。
HaffumanNode *CreateHaffumanTree(HaffumanNode **ppArr,int iArrLen) {HaffumanNode *pSmallNode1 = NULL;HaffumanNode *pSmallNode2 = NULL;HaffumanNode *pNewNode = NULL;/*获取数组的有效长度,只有1 的时候,就直接返回*/while(GetHaffumanArrEffectiveLen(ppArr,iArrLen) > 1){/*先对数组进行排序*/ShellSort(ppArr,iArrLen);//PrintHaffumanArr(ppArr,iArrLen);/*取出最小的两个*/pSmallNode1 = ppArr[0];pSmallNode2 = ppArr[1];/*给最小的两个节点创建一个根节点,建立父子关系*/pNewNode = CreateHaffumanNode();if(pNewNode == NULL){return NULL;}pNewNode->left = pSmallNode1;pNewNode->right = pSmallNode2;pNewNode->weight = pSmallNode1->weight + pSmallNode2->weight;pSmallNode1->parent = pNewNode;pSmallNode2->parent = pNewNode;/*将数组的最小两个节点位置设置成空,并且将新的父节点赋值*/SetHaffumanArrNthValue(ppArr,iArrLen,0,NULL);SetHaffumanArrNthValue(ppArr,iArrLen,1,pNewNode);}return pNewNode; }
第二步是计算路径。因为经历的路径就是这个叶子节点的编码方式。
哈夫曼树其实就是二叉树,使用先序遍历,中序遍历,后续遍历都可以。
和普通的遍历不同的是,需要记录下路径。
我这里使用一个栈来解决这个问题。
int CreateHaffumanTable(HaffumanNode *head,HaffumanStack *pStack) {/*如果是叶子节点,打印当前的路径,并且返回*/if(head->left == NULL && head->right == NULL){printf("weight=%d,value=%c\n",head->weight,head->value);StackPrint(pStack);return -1;}/*如果路径是在左边,就入栈一个1*/StackPush(pStack,'1');CreateHaffumanTable(head->left,pStack);StackPop(pStack);/*如果路径是在右边,就入栈一个0*/StackPush(pStack,'0');CreateHaffumanTable(head->right,pStack);StackPop(pStack);return 0; }
五、整理
完整代码
#include<stdio.h> #include<stdlib.h> #include<string.h>#define MAX_HAFFUMAN_CHAR_TYPE (8) #define MAX_HAFFUMAN_STACK_SIZE (128)typedef struct HaffumanNode {struct HaffumanNode *parent;struct HaffumanNode *left;struct HaffumanNode *right;int weight;int value; }HaffumanNode;typedef struct HaffumanCharMap {char chr;int weight; }HaffumanCharMap;typedef struct HaffumanStack {int qty;int size;char table[MAX_HAFFUMAN_STACK_SIZE+1]; }HaffumanStack;HaffumanStack stack;HaffumanCharMap gMapList[MAX_HAFFUMAN_CHAR_TYPE] = {{'n',55}, {'a',38},{'i',2},{'d',1},{'m',1},{'y',1},{'k',1},{'u',1}, };HaffumanNode *HaffumanArr[MAX_HAFFUMAN_CHAR_TYPE] = {NULL};int StackInit(HaffumanStack *pStack) {pStack->qty = 0;pStack->size = MAX_HAFFUMAN_STACK_SIZE;memset(pStack->table,0x00,pStack->size);return 0; }/*栈是否为空*/ int StackIsEmpty(HaffumanStack *pStack) {if(pStack == NULL){return 1;}return pStack->qty == 0; }/*栈是否满了*/ int StackIsFull(HaffumanStack *pStack) {if(pStack == NULL){return 1;}return pStack->qty == pStack->size; }/*入栈*/ int StackPush(HaffumanStack *pStack,char value) {if(StackIsFull(pStack)){return -1;}pStack->table[pStack->qty] = value;pStack->qty++;return 0; }/*出栈*/ char StackPop(HaffumanStack *pStack) {if(StackIsEmpty(pStack)){return 0;}char value = pStack->table[pStack->qty-1];pStack->table[pStack->qty-1] = '\0';pStack->qty--;return value; }/*打印当前栈区内容*/ int StackPrint(HaffumanStack *pStack) {if(pStack == NULL){return -1;}printf("route is %s\n",pStack->table);return 0; }/*按照权值排序,如果是空的,则放到最后面*/ int compare(HaffumanNode *a,HaffumanNode *b) {if(a == NULL && b == NULL){return 0;}if(a == NULL && b){return 1;}if(a && b==NULL){return -1;}if(a->weight > b->weight){return 1;}if(a->weight == b->weight){return 0;}return -1;}int swap(HaffumanNode **a,HaffumanNode **b) {HaffumanNode *tmp = *a;*a = *b;*b = tmp;return 0; }int InsertSort(HaffumanNode **ppArr,int iArrLen,int gap) {int i = 0;int j = 0;//每个成员都可能是那个3.for(i=0;i<iArrLen;i=i+gap){//从3的位置往前找。for(j=i;j>0;j=j-gap){if(compare(ppArr[j],ppArr[j-gap]) < 0){//还需继续前移swap(&ppArr[j],&ppArr[j-gap]);}else{break;}}} }int ShellCalculateGap(int src_gap) {return src_gap/2; }int ShellSort(HaffumanNode **ppArr,int iArrLen) {int gap = ShellCalculateGap(iArrLen);//第一次的gap是 iArrLen/2,算出来的。while(gap){//使用间距为gap跳着插入,做预处理。如果间距是1了,就会做插入排序。然后就退出了。InsertSort(ppArr,iArrLen,gap);//重新调整间距gap = ShellCalculateGap(gap);}return 0; }HaffumanNode *CreateHaffumanNode() {HaffumanNode *pNode = malloc(sizeof(HaffumanNode));if(pNode == NULL){return NULL;}memset(pNode,0x00,sizeof(HaffumanNode));return pNode; }int DestroyHaffumanNode(HaffumanNode *pNode) {if(pNode){free(pNode);}return 0; }int InitHaffumanArr() {int i = 0;HaffumanNode *p = NULL;for(i = 0;i < sizeof(gMapList)/sizeof(gMapList[0]);i++){p = CreateHaffumanNode();if(p == NULL){exit(1);}p->left = NULL;p->right = NULL;p->parent = NULL;p->weight = gMapList[i].weight;p->value = gMapList[i].chr;HaffumanArr[i] = p;}return 0; }int SetHaffumanArrNthValue(HaffumanNode **ppArr,int iArrLen,int index,HaffumanNode *pNode) {if(ppArr == NULL || index < 0 || index >= iArrLen){return -1;}ppArr[index] = pNode;return 0; }int GetHaffumanArrEffectiveLen(HaffumanNode **ppArr,int iArrLen) {int i = 0;int len = 0;for(i = 0; i < iArrLen; i++){if(ppArr[i] != NULL){len++;}}return len; }int PrintHaffumanArr(HaffumanNode **ppArr,int iArrLen) {int i = 0;for(i = 0; i < iArrLen && ppArr[i] != NULL;i++){printf("ppArr[%d].weight=%d,value=%c\n",i,ppArr[i]->weight,ppArr[i]->value);}return 0; }HaffumanNode *CreateHaffumanTree(HaffumanNode **ppArr,int iArrLen) {HaffumanNode *pSmallNode1 = NULL;HaffumanNode *pSmallNode2 = NULL;HaffumanNode *pNewNode = NULL;/*获取数组的有效长度,只有1 的时候,就直接返回*/while(GetHaffumanArrEffectiveLen(ppArr,iArrLen) > 1){/*先对数组进行排序*/ShellSort(ppArr,iArrLen);//PrintHaffumanArr(ppArr,iArrLen);/*取出最小的两个*/pSmallNode1 = ppArr[0];pSmallNode2 = ppArr[1];/*给最小的两个节点创建一个根节点,建立父子关系*/pNewNode = CreateHaffumanNode();if(pNewNode == NULL){return NULL;}pNewNode->left = pSmallNode1;pNewNode->right = pSmallNode2;pNewNode->weight = pSmallNode1->weight + pSmallNode2->weight;pSmallNode1->parent = pNewNode;pSmallNode2->parent = pNewNode;/*将数组的最小两个节点位置设置成空,并且将新的父节点赋值*/SetHaffumanArrNthValue(ppArr,iArrLen,0,NULL);SetHaffumanArrNthValue(ppArr,iArrLen,1,pNewNode);}return pNewNode; }int TravelHaffumanTree(HaffumanNode *head) {if(head == NULL){return 0;}printf("weight=%d,value=%c\n",head->weight,head->value);TravelHaffumanTree(head->left);TravelHaffumanTree(head->right);return 0; }int CreateHaffumanTable(HaffumanNode *head,HaffumanStack *pStack) {/*如果是叶子节点,打印当前的路径,并且返回*/if(head->left == NULL && head->right == NULL){printf("weight=%d,value=%c\n",head->weight,head->value);StackPrint(pStack);return -1;}/*如果路径是在左边,就入栈一个1*/StackPush(pStack,'1');CreateHaffumanTable(head->left,pStack);StackPop(pStack);/*如果路径是在右边,就入栈一个0*/StackPush(pStack,'0');CreateHaffumanTable(head->right,pStack);StackPop(pStack);return 0; }int main(int argc, const char *argv[]) {/*初始化一个栈*/HaffumanStack stack;StackInit(&stack);/*初始化哈夫曼表*/InitHaffumanArr();/*创建哈夫曼树*/HaffumanNode *head = CreateHaffumanTree(HaffumanArr,MAX_HAFFUMAN_CHAR_TYPE);/*创建的haffuman table*/CreateHaffumanTable(head,&stack);return 0; }
六、方法记忆
- 计算二叉树路径,可以 配合一个栈来解决。
七、参考
- https://blog.csdn.net/qq_29519041/article/details/81428934
- 《大话数据结构》
刻意练习-理解哈夫曼树构建哈夫曼表C语言相关推荐
- 计算WPL·哈夫曼树构建及带权路径长计算
计算WPL·哈夫曼树构建及带权路径长计算 题目信息 输入 输出 测试样例 解答 想法 题目信息 Huffman编码是通信系统中常用的一种不等长编码,它的特点是:能够使编码之后的电文长度最短. 输入 第 ...
- 树:哈夫曼树和哈夫曼编码的详细介绍以及代码实现
闲扯前言 哈夫曼编码的代码实现对于初学数据结构的同学可能会有些困难,没有必要灰心,其实没啥,学习就犹如攀登一座又一座的山峰,每当我们攻克一个难点后,回首来看,也不过如此嘛.我们要做的就是不断的去攀越学 ...
- 【Java数据结构与算法】第十二章 哈夫曼树和哈夫曼编码
第十二章 哈夫曼树和哈夫曼编码 文章目录 第十二章 哈夫曼树和哈夫曼编码 一.哈夫曼树 1.基本术语 2.构建思路 3.代码实现 三.哈夫曼编码 1.引入 2.介绍 3.代码实现哈夫曼编码综合案例 一 ...
- Python数据结构11:树的实现,树的应用,前中后序遍历,二叉查找树BST,平衡二叉树AVL树,哈夫曼树和哈夫曼编码
1.概念 树一种基本的"非线性"数据结构. 相关术语: 节点Node:组成树的基本部分.每个节点具有名称,或"键值",节点还可以保存额外数据项,数据项根据不同的 ...
- 哈夫曼树、哈夫曼编码详解
哈夫曼树介绍 hello,大家好,我是bigsai.本以为哈夫曼树.哈夫曼编码很难,结果很容易嘛! 哈夫曼树.哈夫曼编码很多人可能听过,但是可能并没有认真学习了解,今天这篇就比较详细的讲一下哈夫曼树. ...
- 霍夫曼树及霍夫曼编码的C语言实现,霍夫曼树及霍夫曼编码的C语言实现
从周五开始学习霍夫曼树,一直到今天终于完成,期间遇到了各类各样的棘手的问题,经过一遍遍在纸上分析每一步的具体状态得以解决.如今对学习霍夫曼树的过程加以记录web 首先介绍霍夫曼树数组 霍夫曼树(Huf ...
- 霍夫曼树和霍夫曼编码以及霍夫曼编码的应用
文章目录 霍夫曼树介绍 1.1霍夫曼树的定义 1.2霍夫曼树的几个概念 1.3构建霍夫曼树的过程 1.4代码实现霍夫曼树 霍夫曼编码介绍 什么是霍夫曼编码 通信领域的应用 字符串压缩 1.构造霍夫曼树 ...
- python哈夫曼树_python霍夫曼树
class Node(): data=0 left=None right=None father=None def __init__(self,data,left,right): self.data= ...
- 一文看懂哈夫曼树与哈夫曼编码
转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299884.html 在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUF ...
最新文章
- php输出网络连接,如何打开php文件和输出内容
- FCKeditor的使用说明
- 有图有真相!同是滑屏,荣耀Magic2不只比小米MIX3缝隙小,还更稳定
- elasticSearch6源码分析(10)SettingsModule
- 学校拥有计算机清单和所放位置说明,大学计算机基础期末考试指南(2011)
- AGC023D - Go Home
- Kaseya 修复供应链勒索攻击事件中被利用的缺陷
- 51nod 1050循环数组最大字段和
- Ubuntu 在线升级linux 内核的方法
- tinyxml读xml
- Linux 三个强大的系统资源监控工具
- Labview温度采集系统
- matlab球面投影(二)
- opencv结合微信二维码识别功能实现图片二维码识别
- Tilemap瓦片资源
- 关于html 背景图片的引用格式
- 关于环境变量的理解,maven有没有必要配置MAVEN_HOME或M2_HOME,tomcat一定要设置JAVA_HOME环境变量吗
- android eventlog监听,Android EventLog总结
- NOIP 2016 Day1 T3-换教室
- Fluent Python读书笔记(二)
热门文章
- 堡垒之夜 服务器显示离线,堡垒之夜怎么是离线状态 | 手游网游页游攻略大全...
- 第1章 Linux系统的发展 (一)
- SAP部署SSL数字证书
- JAVA数码宝贝_我的世界1.7.10数码宝贝
- 赛宁网安-r3kapig联合战队冲击DEF CON CTF 2022总决赛
- 湖南大学的计算机网络,林亚平-湖大信息科学与工程学院
- curl: (52) Empty reply from server错误
- 图文详解如何将书签bookmarks导回到chrome?
- 神经了的ODE:Neural Ordinary Differential Equations
- SELinux之一:SELinux基本概念及基本配置