哈夫曼树

构造哈夫曼树
声明单个结点的信息:权值weight,父节点parent,左孩子lc,右孩子rc组成。

以权值分别为4 2 5 9举例

第一阶段:
我们利用数组的方式来构造哈夫曼树,申请2 * n + 1个内存空间用来存储每一个树结点,注意在存储数据的时候从下标为1开始存储。依次将n个权值分别存储在数组中,parent,lc,rc分别赋值为0。

第二阶段:
1、在下标为1-4的权值中找到两个权值最小的,并且父结点为0。
2、将这两个权值相加形成新的权值并赋值给下标为5的树。
3、选取的结点父结点赋值为5.
4、下标为5的左孩子为选取的结点中权值最小的下标,右孩子为另一个结点的下标。

1、在下标为1-5的权值中找到两个权值最小的,并且父结点为0。
2、将这两个权值相加形成新的权值并赋值给下标为6的树。
3、选取的结点父结点赋值为6.
4、下标为6的左孩子为选取的结点中权值最小的下标,右孩子为另一个结点的下标。

1、在下标为1-6的权值中找到两个权值最小的,并且父结点为0。
2、将这两个权值相加形成新的权值并赋值给下标为7的树。
3、选取的结点父结点赋值为7.
4、下标为7的左孩子为选取的结点中权值最小的下标,右孩子为另一个结点的下标。

编码
1、开辟n + 1个空间HC用来存储每一个权值的编码,构造一个辅助的数组来暂时存储权值的编码。
2、将辅助数组的末尾赋值为空,申请一个指针start指向辅助数组的末尾。
3、找到下标为1的树的父结点,如果父结点的左孩子为该树的下标,则辅助数组存储数字0,如果父结点的右孩子为该树的下标,则辅助数组存储数字1。
4、以上一步骤的父结点为新的下标继续重复步骤三,直至父结点为0。
5、将辅助数组的值赋值给HC

例如权值4的编码为111

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef double DataType; //结点权值的数据类型typedef struct HTNode //单个结点的信息
{DataType weight; //权值int parent;        //父节点int lc, rc;        //左右孩子
} * HuffmanTree;typedef char **HuffmanCode; //字符指针数组中存储的元素类型//在下标为1到i-1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值
void Select(HuffmanTree &HT, int n, int &s1, int &s2)//c++中&s1等于传进来的参数可以在函数中对s1进行操作类似于指针的作用
{int min;//找第一个最小值for (int i = 1; i <= n; i++){if (HT[i].parent == 0){min = i;break;}}for (int i = min + 1; i <= n; i++){if (HT[i].parent == 0 && HT[i].weight < HT[min].weight)min = i;}s1 = min; //第一个最小值给s1//找第二个最小值for (int i = 1; i <= n; i++){if (HT[i].parent == 0 && i != s1){min = i;break;}}for (int i = min + 1; i <= n; i++){if (HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1)min = i;}s2 = min; //第二个最小值给s2
}//构建哈夫曼树
void CreateHuff(HuffmanTree &HT, DataType *w, int n)
{int m = 2 * n - 1;                                 //哈夫曼树总结点数HT = (HuffmanTree)calloc(m + 1, sizeof(HTNode)); //开m+1个HTNode,因为下标为0的HTNode不存储数据for (int i = 1; i <= n; i++){HT[i].weight = w[i - 1];//赋权值给n个叶子结点HT[i].lc = 0; HT[i].rc = 0;HT[i].parent = 0;}for (int i = n + 1; i <= m; i++) //构建哈夫曼树{//选择权值最小的s1和s2,生成它们的父结点,s1,s2分别为最小权值和次小权值的下标int s1, s2;Select(HT, i - 1, s1, s2);                      //在下标为1到i-1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值HT[i].weight = HT[s1].weight + HT[s2].weight; // i的权重是s1和s2的权重之和HT[s1].parent = i;                              // s1的父亲是iHT[s2].parent = i;                           // s2的父亲是iHT[i].lc = s1;                               //左孩子是s1HT[i].rc = s2;                                 //右孩子是s2}//打印哈夫曼树中各结点之间的关系printf("哈夫曼树为:>\n");printf("下标   权值     父结点   左孩子   右孩子\n");printf("0                                  \n");for (int i = 1; i <= m; i++){printf("%-4d   %-6.2lf   %-6d   %-6d   %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lc, HT[i].rc);}printf("\n");
}//生成哈夫曼编码
void HuffCoding(HuffmanTree &HT, HuffmanCode &HC, int n)
{HC = (HuffmanCode)malloc(sizeof(char *) * (n + 1)); //开n+1个空间,因为下标为0的空间不用char *code = (char *)malloc(sizeof(char) * n); //辅助空间,编码最长为n(最长时,前n-1个用于存储数据,最后1个用于存放'\0')code[n - 1] = '\0';                                //辅助空间最后一个位置为'\0'for (int i = 1; i <= n; i++){int start = n - 1;    //每次生成数据的哈夫曼编码之前,先将start指针指向'\0'int c = i;            //正在进行的第i个数据的编码int p = HT[c].parent; //找到该数据的父结点while (p)              //直到父结点为0,即父结点为根结点时,停止{if (HT[p].lc == c) //如果该结点是其父结点的左孩子,则编码为0,否则为1code[--start] = '0';elsecode[--start] = '1';c = p;            //继续往上进行编码p = HT[c].parent; // c的父结点}HC[i] = (char *)malloc(sizeof(char) * (n - start)); //开辟用于存储编码的内存空间strcpy(HC[i], &code[start]);                        //将编码拷贝到字符指针数组中的相应位置}free(code); //释放辅助空间
}//主函数
int main()
{int n = 0;printf("请输入数据个数:>");scanf("%d", &n);DataType *w = (DataType *)malloc(sizeof(DataType) * n);if (w == NULL){printf("malloc fail\n");exit(-1);}printf("请输入数据:>");for (int i = 0; i < n; i++){scanf("%lf", &w[i]);}HuffmanTree HT;CreateHuff(HT, w, n); //利用数组构建哈夫曼树HuffmanCode HC;HuffCoding(HT, HC, n); //构建哈夫曼编码for (int i = 1; i <= n; i++) //打印哈夫曼编码{printf("数据%.2lf的编码为:%s\n", HT[i].weight, HC[i]);}free(w);return 0;
}

运算结果

请输入数据个数:>4
请输入数据:>4
2
5
9
哈夫曼树为:>
下标   权值     父结点   左孩子   右孩子
0
1      4.00     5        0        0
2      2.00     5        0        0
3      5.00     6        0        0
4      9.00     7        0        0
5      6.00     6        2        1
6      11.00    7        3        5
7      20.00    0        4        6数据4.00的编码为:111
数据2.00的编码为:110
数据5.00的编码为:10
数据9.00的编码为:0
PS D:\Data Struction\weekFive\student>

哈夫曼树的实际应用

以下为涂然学长的代码,因为该代码运用了文件访问的功能所以在运行是应该在代码相同的路径上创建一个data.txt的文件。code.txt用来存储data文件转换的哈夫曼编码,out.txt用来存储code.txt文件里面的哈夫曼编码的解码数据,另外code.txt和out.txt系统会自动创建不需要自己创建。

/*
Descripttion:
Version: 1.0
Author: 涂然 18计科3班
Date: 2021-05-31 9:00:00
*/#include <iostream>
#include <fstream>
#include <string.h>
using namespace std; #define MaxSize 1024  // 读入文件的上限
#define OK 1
#define ERROR 0
typedef int Status;typedef struct wordcnt{  // 统计字符和对应的次数 char ch;//存储字母int cnt = 0;//一串字符串中ch出现的个数
}Count;typedef struct NumCount{  // 统计次数的外部封装 Count count[MaxSize];//对每一个不同的字母进行封装int length = 0;//不同字母的个数
}NumCount;typedef struct HTree{  // 哈夫曼树结构 char data; int weight;int parent,lchild,rchild;
}HTNode,*HuffmanTree; typedef struct HCode{ // 编码结构 char data;char* str;
}*HuffmanCode;Status ReadData(char *source);  // 读入文件
Status WordCount(char *data,NumCount *paraCnt); // 统计次数
Status Show(NumCount *paraCnt);   // 展示次数
Status CreateHuffmanTree(HuffmanTree &HT,int length,NumCount cntarray);  // 创建哈夫曼树
Status select(HuffmanTree HT,int top,int *s1,int *s2);  // 选择权重最小的两个节点
Status CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int length);  // 创建哈夫曼编码
Status Encode(char *data,HuffmanCode HC,int length);  // 将读入的文件编码,写到txt文件
Status Decode(HuffmanTree HT,int length);  //读入编码文件,解码 int main(int argc, char** argv) {char data[MaxSize];  NumCount Cntarray;ReadData(data);  // 读入数据 WordCount(data,&Cntarray);  // 统计次数 Show(&Cntarray); //可以查看每个单词出现的对应次数 HuffmanTree tree;CreateHuffmanTree(tree,Cntarray.length,Cntarray);  // 建树 HuffmanCode code;  CreateHuffmanCode(tree,code,Cntarray.length);  // 创建编码 Encode(data,code,Cntarray.length);  // 生成编码文件 Decode(tree,Cntarray.length);  // 解码 cout<<"Please view the generated TXT file to check the result"<<endl; return 0;
}Status ReadData(char *source)
{//打开文件读入数据 ifstream infile;infile.open("data.txt");cout<<"Reading..."<<endl;cout<<"the input file is:"<<endl;infile.getline(source,MaxSize);cout<<source<<endl;infile.close();cout<<endl;return OK;
}Status WordCount(char *data,NumCount *paraCnt)
{int flag;// 标识是否已经记录 int len = strlen(data);for(int i = 0;i < len;++i){flag = 0;for(int j = 0;j < paraCnt->length;++j){if(paraCnt->count[j].ch == data[i]) // 若已有记录,直接++ {++paraCnt->count[j].cnt;flag = 1;break;}}if(!flag) // 没有记录,则新增 {paraCnt->count[paraCnt->length].ch = data[i];++paraCnt->count[paraCnt->length].cnt;++paraCnt->length;}}return OK;
}Status Show(NumCount *paraCnt)
{cout<<"the length is "<<paraCnt->length<<endl;for(int i = 0;i < paraCnt->length;++i){cout<<"The character "<<paraCnt->count[i].ch<<"  appears  "<<paraCnt->count[i].cnt<<endl;}cout<<endl;return OK;
}Status CreateHuffmanTree(HuffmanTree &HT,int length,NumCount cntarray)
{if(length <= 1) return ERROR;int s1,s2;int m = length*2-1;  // 没有度为1的节点,则总结点是2*叶子节点数-1个 HT = new HTNode[m+1];    //分配m+1个空间,实际上是m+1个树for(int i = 1;i <= m;++i)  // 初始化 {HT[i].parent = 0;HT[i].lchild = 0;HT[i].rchild = 0;}for(int i = 1;i <= length;++i) {HT[i].data = cntarray.count[i-1].ch;HT[i].weight = cntarray.count[i-1].cnt;}for(int i = length + 1;i <= m;++i){select(HT,i-1,&s1,&s2);  // 从前面的范围里选择权重最小的两个节点 HT[s1].parent = i;HT[s2].parent = i;HT[i].lchild = s1;HT[i].rchild = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;  // 得到一个新节点 }return OK;
}Status select(HuffmanTree HT,int top,int *s1,int *s2)
{int min = INT_MAX;for(int i = 1;i <= top;++i)  // 选择没有双亲的节点中,权重最小的节点 {if(HT[i].weight < min && HT[i].parent == 0){min = HT[i].weight;*s1 = i;}}min = INT_MAX;for(int i = 1;i <= top;++i)  // 选择没有双亲的节点中,权重次小的节点 {if(HT[i].weight < min && i != *s1 && HT[i].parent == 0){min = HT[i].weight;*s2 = i;}}return OK;
}Status CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int length)
{HC = new HCode[length+1];//分配length+1个空间用来存储不同字母对应的编码char *cd = new char[length];  // 存储编码的临时空间 cd[length-1] = '\0';  // 方便之后调用strcpy函数 int c,f,start;for(int i = 1;i <= length;++i){start = length-1;  // start表示编码在临时空间内的起始下标,由于是从叶子节点回溯,所以是从最后开始 c = i;f = HT[c].parent;while(f != 0){--start;  // 由于是回溯,所以从临时空间的最后往回计 if(HT[f].lchild == c)cd[start] = '0';else cd[start] = '1';c = f;f = HT[c].parent;}HC[i].str = new char[length-start];  // 最后,实际使用的编码空间大小是length-start HC[i].data = HT[i].data;strcpy(HC[i].str,&cd[start]);  // 从实际起始地址开始,拷贝到编码结构中 }delete cd;
}Status Encode(char *data,HuffmanCode HC,int length)
{ofstream outfile;outfile.open("code.txt");for(int i = 0;i < strlen(data);++i)  // 依次读入数据,查找对应的编码,写入编码文件 {for(int j = 1;j <= length;++j){if(data[i] == HC[j].data){outfile<<HC[j].str;}}}outfile.close();cout<<"the code txt has been written"<<endl;cout<<endl;return OK;
}Status Decode(HuffmanTree HT,int length)
{char *codetxt = new char[MaxSize * length];ifstream infile;infile.open("code.txt");infile.getline(codetxt,MaxSize*length);infile.close();ofstream outfile;outfile.open("out.txt");int root = 2*length-1;  // 从根节点开始遍历 for(int i = 0;i < strlen(codetxt);++i){if(codetxt[i] == '0') root = HT[root].lchild;  //为0表示向左遍历 else if(codetxt[i] == '1') root = HT[root].rchild; //为1表示向右遍历 if(HT[root].lchild == 0 && HT[root].rchild == 0)  // 如果已经是叶子节点,输出到输出文件中,然后重新回到根节点 {outfile<<HT[root].data;root = 2*length-1;}}outfile.close();cout<<"the output txt has been written"<<endl;cout<<endl;return OK;
}

运行结果

 ^
Reading...
the input file is:
Life is full of confusing and disordering Particular time,a particular location,Do the arranged thing of ten million time in the brain,Step by step ,the life is hard to avoid delicacy and stiffness No enthusiasm forever,No unexpected happening of surprising and pleasing So,only silently ask myself in mind Next happiness,when will come?the length is 31
The character L  appears  1
The character i  appears  30
The character f  appears  11
The character e  appears  28
The character    appears  50
The character s  appears  18
The character u  appears  7
The character l  appears  16
The character o  appears  17
The character c  appears  8
The character n  appears  26
The character g  appears  7
The character a  appears  20
The character d  appears  11
The character r  appears  14
The character P  appears  1
The character t  appears  18
The character m  appears  7
The character ,  appears  7
The character p  appears  10
The character D  appears  1
The character h  appears  9
The character b  appears  2
The character S  appears  2
The character y  appears  5
The character v  appears  2
The character N  appears  3
The character x  appears  2
The character k  appears  1
The character w  appears  2
The character ?  appears  1the code txt has been writtenthe output txt has been writtenPlease view the generated TXT file to check the result
PS D:\Data Struction\weekFive\teacher>

C语言数据结构_哈夫曼树相关推荐

  1. 数据结构源码笔记(C语言):哈夫曼树

    #include <stdio.h> #include <stdlib.h> #define MAXINT 2147483647 #define MAXNUM 50 #defi ...

  2. c语言霍夫曼函数,使用C语言详解霍夫曼树数据结构

    1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,--,kj, 使得 ki是ki+1 的双亲(1<=i 从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它 ...

  3. 数据结构“基于哈夫曼树的数据压缩算法”的实验报告

    一个不知名大学生,江湖人称菜狗 original author: jacky Li Email : 3435673055@qq.com Last edited: 2022.11.20 目录 数据结构& ...

  4. 数据结构(哈夫曼树+KMP)之 数据加密+解密

    数据结构(哈夫曼树+KMP)之 数据加密+解密 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> #define N 1 ...

  5. 2020-10-1 //严蔚敏《数据结构》 //赫夫曼树及其应用:创建顺序赫夫曼树创建及得到赫夫曼编码

    //严蔚敏<数据结构> //赫夫曼树及其应用:创建顺序赫夫曼树创建及得到赫夫曼编码 //(从叶子结点到根逆向求每个字符的赫夫曼编码)以及(无栈非递归遍历赫夫曼树,求赫夫曼编码) //自学中 ...

  6. 数据结构 基于哈夫曼树的数据压缩算法

    数据结构 基于哈夫曼树的数据压缩算法 实验目的 实验内容 实验提示 实验代码 实验小结 实验目的 1.掌握哈夫曼树的构造算法. 2.掌握哈夫曼编码的构造算法. 实验内容 问题描述 输入一串字符串,根据 ...

  7. 最优生成树c语言_C语言:数据结构-构造哈夫曼树

    给定n个权值的集合W={w1,w2,-.wn} 1.在W中选取两个最小的权作为兄弟结点,以它们的权值之和作为其父结点,得到一棵新树: 2.在W中删除上述已选取的权值,以它们的权值之和作为新的权值加入W ...

  8. labview 霍夫曼树_哈夫曼树编码实验报告_信息论与编码实验2 实验报告_信息论与编码报告...

    huffman编码C语言实验报告 今日推荐 180份文档 2014...4页 1下载券 安卓版100 doors 2攻略1... 3页 1下载券 <逃脱本色>doors....语文教育实习 ...

  9. 【数据结构】赫夫曼树

    数据结构赫夫曼树 /*名称:赫夫曼树语言:数据结构C语言版 编译环境:VC++ 6.0日期: 2014-3-26 */#include <stdio.h> #include <lim ...

最新文章

  1. Paddle 环境中 使用LeNet在MNIST数据集实现图像分类
  2. 【转载】程序员有哪些电脑技能让外行感到神奇?
  3. 简单了解线程和进程、多进程和多线程、并发和并行的区别
  4. python 服务器框架_python 服务器框架
  5. 判断回文串时忽略既非字母又非数字的字符
  6. CRNN+CTCLoss中文手写汉字识别
  7. 用递归方法求一个list的最大值
  8. 基金侧袋机制: 指引与操作规范
  9. PS去掉图片上的文字
  10. 推荐 15个 React 图标库
  11. Android游戏开发之小球重力感应实现
  12. 新安装Visio2013每次打开都提示正在配置,解决办法
  13. GDDR6X和GDDR6差距对比 GDDR6X和GDDR6区别
  14. 一个元素位于另一个元素之上,点击上面的元素引发下面元素事件操作
  15. 51 《格鲁夫给经理人的第一课》 -豆瓣评分8.8
  16. taskkill掉带空格的windowtitle
  17. 音视频技术开发周刊 62期
  18. 先有鸡还是先有蛋,程序员怎么看
  19. 【工欲善其事必先利其器·单点登录】使用CAS WAR Overlays部署CAS
  20. Android6.0 APN

热门文章

  1. 用 Pyecharts 可视化微信好友
  2. 除了性能缩水还有啥问题?盘点iOS升级的大坑
  3. 5 单选题 计算机病毒是指( ),计算机病毒指的是事业单位考试题库考点高频试题:1、单选题秦汉(5)...
  4. C#数据库编程入门:病人管理系统
  5. 朴实无华,图解快排,多语言实现。(PS:还有宝藏资料)
  6. c++读取逗号分隔输入_pandas-csv的读取与导出(全网最详细版本,含代码和实例)...
  7. java面试app_Java面试训练app下载-Java面试题训练下载 v2.3 安卓版-IT猫扑网
  8. 【Linux】基本指令合集
  9. 【模拟】蚂蚁(jzoj 1508)
  10. 用Modelsim仿真QII FFT IP核的时候出现的Error: Illegal target for defparam