理解哈夫曼树构建哈夫曼表

一、哈夫曼树的作用

  • 哈夫曼树是一个二叉树,是可以将一些字节重新编码 ,而且能够使用最少的空间。所以也叫最优二叉树。

  • 比如这段字符串

    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语言相关推荐

  1. 计算WPL·哈夫曼树构建及带权路径长计算

    计算WPL·哈夫曼树构建及带权路径长计算 题目信息 输入 输出 测试样例 解答 想法 题目信息 Huffman编码是通信系统中常用的一种不等长编码,它的特点是:能够使编码之后的电文长度最短. 输入 第 ...

  2. 树:哈夫曼树和哈夫曼编码的详细介绍以及代码实现

    闲扯前言 哈夫曼编码的代码实现对于初学数据结构的同学可能会有些困难,没有必要灰心,其实没啥,学习就犹如攀登一座又一座的山峰,每当我们攻克一个难点后,回首来看,也不过如此嘛.我们要做的就是不断的去攀越学 ...

  3. 【Java数据结构与算法】第十二章 哈夫曼树和哈夫曼编码

    第十二章 哈夫曼树和哈夫曼编码 文章目录 第十二章 哈夫曼树和哈夫曼编码 一.哈夫曼树 1.基本术语 2.构建思路 3.代码实现 三.哈夫曼编码 1.引入 2.介绍 3.代码实现哈夫曼编码综合案例 一 ...

  4. Python数据结构11:树的实现,树的应用,前中后序遍历,二叉查找树BST,平衡二叉树AVL树,哈夫曼树和哈夫曼编码

    1.概念 树一种基本的"非线性"数据结构. 相关术语: 节点Node:组成树的基本部分.每个节点具有名称,或"键值",节点还可以保存额外数据项,数据项根据不同的 ...

  5. 哈夫曼树、哈夫曼编码详解

    哈夫曼树介绍 hello,大家好,我是bigsai.本以为哈夫曼树.哈夫曼编码很难,结果很容易嘛! 哈夫曼树.哈夫曼编码很多人可能听过,但是可能并没有认真学习了解,今天这篇就比较详细的讲一下哈夫曼树. ...

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

    从周五开始学习霍夫曼树,一直到今天终于完成,期间遇到了各类各样的棘手的问题,经过一遍遍在纸上分析每一步的具体状态得以解决.如今对学习霍夫曼树的过程加以记录web 首先介绍霍夫曼树数组 霍夫曼树(Huf ...

  7. 霍夫曼树和霍夫曼编码以及霍夫曼编码的应用

    文章目录 霍夫曼树介绍 1.1霍夫曼树的定义 1.2霍夫曼树的几个概念 1.3构建霍夫曼树的过程 1.4代码实现霍夫曼树 霍夫曼编码介绍 什么是霍夫曼编码 通信领域的应用 字符串压缩 1.构造霍夫曼树 ...

  8. python哈夫曼树_python霍夫曼树

    class Node(): data=0 left=None right=None father=None def __init__(self,data,left,right): self.data= ...

  9. 一文看懂哈夫曼树与哈夫曼编码

    转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299884.html 在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUF ...

最新文章

  1. php输出网络连接,如何打开php文件和输出内容
  2. FCKeditor的使用说明
  3. 有图有真相!同是滑屏,荣耀Magic2不只比小米MIX3缝隙小,还更稳定
  4. elasticSearch6源码分析(10)SettingsModule
  5. 学校拥有计算机清单和所放位置说明,大学计算机基础期末考试指南(2011)
  6. AGC023D - Go Home
  7. Kaseya 修复供应链勒索攻击事件中被利用的缺陷
  8. 51nod 1050循环数组最大字段和
  9. Ubuntu 在线升级linux 内核的方法
  10. tinyxml读xml
  11. Linux 三个强大的系统资源监控工具
  12. Labview温度采集系统
  13. matlab球面投影(二)
  14. opencv结合微信二维码识别功能实现图片二维码识别
  15. Tilemap瓦片资源
  16. 关于html 背景图片的引用格式
  17. 关于环境变量的理解,maven有没有必要配置MAVEN_HOME或M2_HOME,tomcat一定要设置JAVA_HOME环境变量吗
  18. android eventlog监听,Android EventLog总结
  19. NOIP 2016 Day1 T3-换教室
  20. Fluent Python读书笔记(二)

热门文章

  1. 堡垒之夜 服务器显示离线,堡垒之夜怎么是离线状态 | 手游网游页游攻略大全...
  2. 第1章 Linux系统的发展 (一)
  3. SAP部署SSL数字证书
  4. JAVA数码宝贝_我的世界1.7.10数码宝贝
  5. 赛宁网安-r3kapig联合战队冲击DEF CON CTF 2022总决赛
  6. 湖南大学的计算机网络,林亚平-湖大信息科学与工程学院
  7. curl: (52) Empty reply from server错误
  8. 图文详解如何将书签bookmarks导回到chrome?
  9. 神经了的ODE:Neural Ordinary Differential Equations
  10. SELinux之一:SELinux基本概念及基本配置