哈夫曼树的压缩VC++程序及其效果

哈夫曼树的压缩VC++程序及其效果
哈夫曼树的压缩VC++程序及其效果

使用huffman编码压缩文件-课设成果

问题描述:哈夫曼是一种常用的压缩方法。是1952年为文本文件建立的,其基本原理是频繁使用的数据用较短的代码代替,很少使用的数据用较长的代码代替,每个数据的代码各不相同。这些代码都是二进制码,且码的长度是可变的。如: 有一个原始数据序列,ABACCDAA则编码为A(0),B(10),C(110),(D111),压缩后为010011011011100。产生霍夫曼编码需要对原始数据扫描两遍,第一遍扫描要精确地统计出原始数据中的每个值出现的频率,第二遍是建立霍夫曼树并进行编码,由于需要建立二叉树并遍历二叉树生成编码,因此数据压缩和还原速度都较慢,但简单有效,因而得到广泛的应用。哈夫曼编码是无损压缩当中最好的方法。它使用预先二进制描述来替换每个符号,长度由特殊符号出现的频率决定。常见的符号需要很少的位来表示,而不常见的符号需要很多为来表示。哈夫曼算法在改变任何符号二进制编码引起少量密集表现方面是最佳的。然而,它并不处理符号的顺序和重复或序号的序列哈夫曼压缩,首先用ASCII值初始化511个哈夫曼节点,然后,计算在输入缓冲区数据中,每个ASCII码出现的频率。然后,根据频率进行排序,现在,构造哈夫曼树,获取每个ASCII码对应的位序列,构造哈夫曼树,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。这样,新节点就是两个被替换节点的父节点了。如此循环,直到队列中只剩一个节点(树根)。压缩的最后一步是将每个ASCII编码写入输出缓冲区中哈夫曼解压缩,将输入缓冲区中的每个编码用对应的ASCII码逐个替换就可以了。只要记住,这里的输入缓冲区是一个包含每个ASCII 值的编码的位流。因此,为了用ASCII值替换编码,我们必须用位流搜索哈夫曼树,直到发现一个叶节点,然后将它的ASCII值添加到输出缓冲区中:

复制内容到剪贴板

/**************************************************************** */

/**************************************************************** */

/**程序: compress.c **/ /**功能: 压缩与解压缩单个文件 **/

/**详情: 利用hufffman算法生成输入文件的huffman编码,并转换成二进制字节 **/

/** 流输出从而达到压缩的效果;解压缩则是从压缩文件中读入huffman 树 **/

/** 信息,从而还原为原编码,达到解压缩的目

的。 **/

/** 压缩文件结构 **/

/** 偏移量存储信息类型 **/

/** 0~3 文件头标志 **/

/** 4 源文件名长度:n_s **/

/** 5~4+n_s 文件名 **/

/** 5+n_s~8+n_s 源文件长度 **/

/** 9+n_s~1028+n_s huffman树非叶子节点孩子信息 **/

/** 1029+n_s~FILE_END 源文件的huffman二级制编码信息 **/

/**环境: AM2_4000+ & Windows_Server_2008 & VC++_2008 **/

/**其他: davelv @ 2008-12-18 **/

/**************************************************************** */

/**************************************************************** */

///////////////////////////////////////////////////////////////// ///

//// 头文件、自定义类型、预定义宏常量 //// ///////////////////////////////////////////////////////////////// ///

#define _CRT_SECURE_NO_DEPRECATE//VC2005或更新的编译器不显示I/O安全警告

#include

#include

#include

typedef unsigned int UINT;

typedef unsigned char UCHAR;

typedef unsigned short USHORT;

typedef struct node

{

long w;//权

short p,l,r;//父亲,左孩子,右孩子

}htnode,*htnp;//霍夫曼树的结点

typedef struct huffman_code

{

UCHAR len;//长度

UCHAR *codestr;//字符串

}hufcode;//字符版本的霍夫曼编码

#define OK 1

#define ERROR -1

#define UNUSE -1

#define ARGS_ERR -2//参数错误

#define FILE_ERR -3//文件错误

#define HEAD_ERR -4//头标错误

#define MALLOC_ERR -5//内存分配错误#define HUFTREE_ERR -6//霍夫曼树错误

#define HUFCODE_ERR -7//霍夫曼编码错误

#define CHAR_BITS 8//一个字符中的位数

#define INT_BITS 32//一个整型中的位数

#define CODE_SIZE 256//霍夫曼编码个数

#define CACHE_SIZE 256//I/O缓存大小

#define UINT_SIZE sizeof(UINT)

#define UCHAR_SIZE sizeof(UCHAR) #define USHORT_SIZE sizeof(USHORT)

#define ZIP_HEAD 0xFFFFFFFF//压缩文件头标

///////////////////////////////////////////////////////////////// ///

//// 函数声明 ////

///////////////////////////////////////////////////////////////// ///

//压缩相关函数

UCHAR chars_to_bits(const UCHAR chars[CHAR_BITS]);//将一个字符数组转换成二进制数字@ write_compress_file()

int search_set(htnp ht,int n);//查找当前最小权值的霍夫曼树节点并置为占用@create_huffmantree()

int create_huffmantree(long w[],int n,htnode ht[]);//生成霍夫曼树@compress()

int encode_huffmantree(htnp htp,int n,hufcode hc[]);//生成霍夫曼编码@compress()

long calc_data_frequency(FILE *in,long frequency[]);//计算每个不同字节的频率以及所有的字节数@compress()

int compress(char *source_filename,char *obj_filename);//处理压缩文件的流程@process_args()

int c_initial_files(char *source_filename,FILE **inp,char *obj_filename,FILE **outp);//为处理压缩流程初始化文件@compress()

int write_compress_file(FILE *in,FILE *out,htnp ht,hufcode hc[],char* source_filename,long source_filesize);//写压缩文件@compress()

//解压缩相关函数

void get_mini_huffmantree(FILE* in,short mini_ht[][2]);//从待解压缩文件中得到一个小型的huffman树@decompress()

int decompress(char *source_filename,char *obj_filename);//处理解压缩文件的流程@process_args()

int d_initial_files(char *source_filename,FILE **inp,char *obj_filename,FILE **outp);//为处理解压缩流程初始化文件@decompress()

int write_decompress_file(FILE *in,FILE* out,short mini_ht[][2],long bits_pos,long obj_filesize);//写解压缩文件@decompress()

//辅助函数

void print_help();//在屏幕上显示帮助@process_args(),main()

void process_error(int error_code);//处理函数中返回的错误代码@process_args(),main()

void process_args(char *first,char*second,char*third);//处理命令行参数@main()

char *create_default_obj_filename(char *source_filename,char* obj_filename);//生成一个默认的文件名@c_initial_files()

///////////////////////////////////////////////////////////////// ///

//// 压缩相关函数 ////

///////////////////////////////////////////////////////////////// ///

/**************************************************************** /

/*函数: c_initial_files() */

/*功能: 初始化并打开压缩功能所需所有文件 */ /*参数: char *source_filename 源文件名字符串 */

/* FILE **inp 指向输入文件指针的指针 */

/* char *obj_filename 目标文件名字字符串 */

/* FILE **outp 指向输出文件指针的指针 */

/*返回: int OK 函数成功运行 */

/* 其他出错 */

/**************************************************************** /

int c_initial_files(char *source_filename,FILE **inp,char *obj_filename,FILE **outp)

{

if(source_filename==NULL)

{

return FILE_ERR;//空名字返回错误

}

//当目标文件名没分配时

if(obj_filename==NULL)

{

if((obj_filename=(char*)malloc(256*sizeof(char)))= =NULL)

{

return MALLOC_ERR;//分配名字空间失败,返回错误

}

create_default_obj_filename(source_filename,obj_fi lename);//生成默认名字

}

if(strcmp(source_filename,obj_filename)==0)

{

return FILE_ERR;//重名返回错误

}

printf("待压缩文件:%s,压缩文件:%s\n",source_filename,obj_filename);

if((*outp=fopen(obj_filename,"wb"))==NULL)

{

return FILE_ERR;//不能读,返回错误

}

if((*inp=fopen(source_filename,"rb"))==NULL)

{

return FILE_ERR;//不能写,返回错误

}

free(obj_filename);//清理堆

return OK;

}

/**************************************************************** /

/*函数: calc_data_frequency() */ /*功能: 计算输入文件中不同字节的出现的次数以及所有的字节数 */

/*参数: FILE *in 输入文件指针 */

/* long frequency[] 保存不同字节出现次数的数组 */

/*返回: long filesize 输入文件总长度 */

/**************************************************************** /

long calc_data_frequency(FILE *in,long frequency[])

{

int i,read_len;

UCHAR buf[CACHE_SIZE];//I/O缓存数组

long filesize;//总长

for(i=0;i

{

frequency=0;//初始化频率为0

}

fseek(in,0L,SEEK_SET);

read_len=CACHE_SIZE; //设置读入长度

while(read_len==CACHE_SIZE)

{

read_len=fread(buf,1,CACHE_SIZE,in);

for(i=0;i

{

frequency[*(buf+i)]++;//计算每个字符出现次数

}

}

for(i=0,filesize=0;i

{

filesize+=frequency;//计算全部字节数

}

return filesize;

}

/**************************************************************** /

/*函数: search_set() */ /*功能: 查找当前最小权值的霍夫曼树节点并置为占用 */

/*参数: htnp ht huffman树的指针 */

/* int n 查找范围 */

/*返回: int x 最小权值节点的下标 */

/**************************************************************** /

int search_set(htnp ht,int n)

{

int i,x;

for(x=0;x

{

if(ht[x].p==UNUSE) break;//找到第一个叶子节点,跳出

}

for(i=x;i

{

if(ht.p==UNUSE&&ht.w

{

x=i;//找权值最小的叶子节点

}

}

ht[x].p=-2;//将叶子节点占用

return x;

}

/**************************************************************** /

/*函数: create_huffmantree() */

/*功能: 通过给定的数据权值生成霍夫曼树 */

/*参数: long w[] 数据的权值 */

/* int n 数据的个数 */

/* htnp ht huffman树的指针 */

/*返回: int OK 函数成功运行 */

/* 其他出错 */ /**************************************************************** /

int create_huffmantree(long w[],int n,htnode ht[])

{

int m,i,s1,s2;

if (n<1) return HUFTREE_ERR;

m=2*n-1;//霍夫曼树节点总数=叶子数*2-1

if (ht==NULL) return HUFTREE_ERR;

for(i=0;i

{

ht.w=w,ht.p=ht.l=ht.r=UNUSE;//初始化叶子节点

}

for(;i

{

ht.w=ht.p=ht.l=ht.r=UNUSE;//初始化非叶子节点

}

for(i=n;i

{

s1=search_set(ht,i);

s2=search_set(ht,i);//找到权值最小的两个节点

ht[s1].p=ht[s2].p=i;//设置父节点

ht.l=s1,ht.r=s2;//设置孩子

ht.w=ht[s1].w+ht[s2].w;//设置权

}

return OK;

}

/**************************************************************** /

/*函数: encode_huffmantree() */

/*功能: 通过给定的霍夫曼树生成霍夫曼编码 */ /*参数: htnp htp huffman树的指针 */

/* int n 数据的个数 */

/* huffcode hc[] 霍夫曼编码存储数组 */

/*返回: int OK 函数成功运行 */

/* 其他出错 */

/**************************************************************** /

int encode_huffmantree(htnp htp,int n,hufcode hc[])

{

int i,j,p,codelen;//codelen:临时字符数组长度

UCHAR *code=(UCHAR*)malloc(n*UCHAR_SIZE);//临时字符数组

if (code==NULL) return MALLOC_ERR;

for(i=0;i

{

//从当前节点到根节点逆向求huffman编码

for(p=i,codelen=0;p!=2*n-2;p=htp[p].p,codelen++)

{

code[codelen]=(htp[htp[p].p].l==p?0:1);//左孩子:0;右孩子:1

}

if((hc.codestr=(UCHAR *)malloc((codelen)*UCHAR_SIZE))==NULL)

{

return MALLOC_ERR;//分配叶子节点huffman编码的空间

}

hc.len=codelen;//码长

for(j=0;j

{

hc.codestr[j]=code[codelen-j-1];//转换为正向huffman编码

}

}

free(code);//清理临时字符数组

return OK;

}

/**************************************************************** /

/*函

数: chars_to_bits() */ /*功能: 将一组(8)个字符转换为二进制数 */

/*参数: const UCHAR chars[] 待转换的字符数组 */

/*返回: UCHAR bits 转换后的二进制数 */

/**************************************************************** /

UCHAR chars_to_bits(const UCHAR chars[CHAR_BITS])

{

int i;

UCHAR bits=0;

bits|=chars[0];

for(i=1;i

{

bits<<=1;

bits|=chars;

}

return bits;

}

/**************************************************************** /

/*函数: write_compress_file() */ /*功能: 转换源文件为二进制huffman编码以及其他信息输出到目标文件 */

/*参数: FILE *in 输入文件的指针 */

/* FILE *out 输出文件的指针 */

/* htnp ht 霍夫曼树的指针 */

/* hufcode hc[] 霍夫曼编码数组 */

/* char *source_filename 源文件名字符串 */

/* long source_filesize 源文件长度 */

/*返回: int OK 函数成功运行 */

/* 其他出错 */

/**************************************************************** /

int write_compress_file(FILE *in,FILE *out,htnp ht,hufcode hc[],char* source_filename,long source_filesize)

{

UINT i,read_counter,write_counter,zip_head=ZIP_HEAD ;//读缓存计数器,写缓存计数器,压缩文件头标

UCHAR write_char_counter,code_char_counter,copy_cha r_counter,//写字符计数器,huffman码字符计数器,复制字符计数器

read_buf[CACHE_SIZE],write_buf[CACHE_SIZE], write_chars[CHAR_BITS],filename_size=strlen(source_filename);//读缓存,写缓存,转换字符数组,文件名长度

hufcode *cur_hufcode;//当前数据的huffman编码指针

//定位读写文件到文件开始处

fseek(in,0L,SEEK_SET);

fseek(out,0L,SEEK_SET);

//写文件头

fwrite(&zip_head,UINT_SIZE,1,out);//写入文件头标志

//写源文件名

fwrite(&filename_size,UCHAR_SIZE,1,out);//写入源文件名长度 fwrite(source_filename,sizeof(char),filename_size,out);//写入源文件名

//写入源文件长度

fwrite(&source_filesize,sizeof(long),1,out);

//写huffman树的左右孩子(前CODE_SIZE个节点无孩子,不写)

for(i=CODE_SIZE;i

{

fwrite(&(ht.l),sizeof(ht.l),1,out);//写入左孩子

fwrite(&(ht.r),sizeof(ht.r),1,out);//写入右孩子

}

//写编码后信息

write_counter=write_char_counter=0;//写缓冲计数器以及写字符计数器清0

read_counter=CACHE_SIZE;//置读缓存字符数

//当读入的缓存字符数不等于缓存时,文件读完,退出循环

while(read_counter==CACHE_SIZE)

{

read_counter=fread(read_buf,1,CACHE_SIZE,in);//读入数据到读缓存

//为每个缓存的数据找huffman编码

for(i=0;i

{

cur_hufcode=&hc[read_buf];//当前数据的huffman编码位置

code_char_counter=0;//当前数据huffman编码字符的计数器清0

//当huffman编码字符的计数器等于码长时,转换完毕退出

while(code_char_counter!=cur_hufcode->len) { //获取本次复制字符的长度为可用写字符长度与可用huffman编码长度中的较小者

copy_char_counter= (CHAR_BIT S-write_char_counter > cur_hufcode->len-code_char_counter ?

cur_hufcode->len-code_char_counter : CHAR_BITS-write_char_counter);

//复制一段字符

memcpy(write_chars+write_char_count er,cur_hufcode->codestr+code_char_counter,copy_char_counter);

write_char_counter+=copy_char_count er;//写字符计数器增加

code_char_counter+=copy_char_counte r;//编码字符计数器增加

//当写字符计算器满=8时

if(write_char_counter==CHAR_BITS)

{

write_char_counter=0;//写字符

计数器清0

//当写缓存满时

write_buf[write_counter++]=c hars_to_bits(write_chars);//转化写字符为二进制数并存入写缓存

if(write_counter==CACHE_SIZE) {

fwrite(write_buf,1,C ACHE_SIZE,out);//输出到文件

write_counter=0;//写

缓存清0

}

}

}

}

}

fwrite(write_buf,1,write_counter,out);//写缓存中剩余数据输出

到文件

//当写字符数组中还有字符未转换

if(write_char_counter!=0)

{

write_char_counter=chars_to_bits(write_chars);//转

化为二级制数据

fwrite(&write_char_counter,1,1,out);//输出到文件

}

return OK;

}

/****************************************************************

/

/*函数: compress() */

/*功能: 处理压缩文件的流

程 */

/*参数: char *source_filename 源文件名字符

串 */

/* char *obj_filename 目标文件名字字符串 */

/*返回: int OK 函数成功运行 */

/* 其他出错 */

/**************************************************************** /

int compress(char *source_filename,char *obj_filename)

{

FILE *in,*out;

int error_code,i;

float compress_rate;

hufcode hc[CODE_SIZE];

htnode ht[CODE_SIZE*2-1];

long frequency[CODE_SIZE ],source_filesize,obj_filesize=0; //处理待压缩与压缩文件文件

error_code=c_initial_files(source_filename,&in,obj_filenam e,&out);

if(error_code!=OK)

{

return error_code;

}

puts("文件打开成功,开始读取文件信息...");

//处理各个字符的频率

source_filesize=calc_data_frequency(in,frequency); puts("文件读入完成,开始分析文件信息...");

printf("文件大小 %ld 字节\n",source_filesize);

//生成huffmantree

error_code=create_huffmantree(frequency,CODE_SIZE,ht);

if(error_code!=OK)

{

return error_code;

}

puts("霍夫曼树建立成功,开始霍夫曼编码...");

//生成huffmancode

error_code=encode_huffmantree(ht,CODE_SIZE,hc);

if(error_code!=OK)

{

return error_code;

}

//处理压缩文件长度

for(i=0;i

obj_filesize+=frequency*hc.len;//计算位的个数

obj_filesize=obj_filesize%8==0?obj_filesize/8:obj_filesize /8+1;//转换为字节个数;

for(i=0;i

obj_filesize+=2*sizeof(short);//huffmantree长度

obj_filesize+=strlen(source_filename)+1;//源文件名长度

obj_filesize+=sizeof(long);//源文件长度

obj_filesize+=UINT_SIZE;//文件头长度

compress_rate=(float)obj_filesize/source_filesize;

puts("编码成功,开始生成压缩文件...");

printf("压缩文件预期长度:%ld字节,压缩比例:%%%.2lf\n",obj_filesize,compress_rate*100);

//生成压缩文件

error_code=write_compress_file(in,out,ht,hc,source_filenam e,source_filesize);

if(error_code!=OK)

{

return error_code;

}

puts("压缩完成!");

//清理资源

fclose(in);

fclose(out);//关闭文件

for(i=0;i

{

free(hc.codestr);

}

return OK;

}

///////////////////////////////////////////////////////////////// ///

//// 解压缩相关函数函 ////

///////////////////////////////////////////////////////////////// ///

/**************************************************************** /

/*函数: d_initial_files() */

/*功能: 初始化并打开压缩功能所需所有文件 */ /*参数: char *source_filename 源(待解压缩)文件名字符串 */

/* FILE **inp 指向输入文件指针的指针 */

/* char *obj_filename 目标(解压缩)文件名字字符串 */

/* FILE **outp 指向输出文件指针的指针 */

/*返回: int OK 函数成功运行 */

/* 其他出错 */

/**************************************************************** /

int d_initial_files(char *source_filename,FILE **inp,char *obj_filename,FILE **outp)

{

UINT zip_head;

UCHAR filename_size;

printf("待解压缩文件:%s,",source_filename);

if ((*inp=fopen(source_filename,"rb"))==NULL)

{

return FILE_ERR;//不能读文件

}

//处理文件头

fread(&zip_head,UINT_SIZE,1,*inp);

if(zip_head!=ZIP_HEAD)

{

return HEAD_ERR;//非法的文件头

}

//处理解压缩文件名

if(obj_filename==NULL)//如果目标文件名未分配

{

if((obj_filename=(char*)malloc(256*sizeof(char)))= =NULL)

{

return MALLOC_ERR;//内存分配失败

}

fread(&filename_size,UCHAR_SIZE,1,*inp);//得到目标文件名长度

fread(obj_filename,sizeof(char),filename_size,*inp );//得到目标文件名

obj_filename[filename_size]='\0';//添加结尾字符

}

else

{

fread(&filename_size,UCHAR_SIZE,1,*inp);

fseek(*inp,filename_size,SEEK_CUR);//若分配了,直接跳过文件名信息

}

printf("解压缩文件:%s\n",obj_filename);

if((*outp=fopen(obj_filename,"wb"))==NULL)

{

return FILE_ERR;//不能写文件

}

free(obj_filename);//清理堆

return OK;

}

/****************************************************************

/

/*函数: get_mini_huffmantree() */ /*功能: 从输入文件得到一个小型的huffman 树 */

/*参数: FILE *in 输入文件指针 */

/* short mini_ht[][2] 存储小型huffman树的数组 */

/*返回: 无 */

/**************************************************************** /

void get_mini_huffmantree(FILE* in,short mini_ht[][2])

{

int i;

for(i=0;i

{

mini_ht[0]=mini_ht[1]=UNUSE;//叶子节点无孩子

}

fread(mini_ht,sizeof(short),2*(CODE_SIZE-1),in);//得到非叶子节点的孩子信息

}

/**************************************************************** /

/*函数: write_decompress_file() */ /*功能: 转换压缩文件的二进制huffman编码为源编码并输出到目标文件 */

/*参数: FILE *in 输入文件的指针 */

/* FILE *out 输出文件的指针 */

/* short mini_ht[][2] 存储小心霍夫曼树数组 */

/* long bits_pos 输入文件压缩码开始处的偏移量 */

哈夫曼树编码译码实验报告(DOC)

数据结构课程设计设计题目:哈夫曼树编码译码

目录 第一章需求分析 (1) 第二章设计要求 (1) 第三章概要设计 (2) (1)其主要流程图如图1-1所示。 (3) (2)设计包含的几个方面 (4) 第四章详细设计 (4) (1)①哈夫曼树的存储结构描述为: (4) (2)哈弗曼编码 (5) (3)哈弗曼译码 (7) (4)主函数 (8) (5)显示部分源程序: (8) 第五章调试结果 (10) 第六章心得体会 (12) 第七章参考文献 (12) 附录: (12)

在当今信息爆炸时代,如何采用有效的数据压缩技术节省数据文件的存储空间和计算机网络的传送时间已越来越引起人们的重视,哈夫曼编码正是一种应用广泛且非常有效的数据压缩技术。哈夫曼编码是一种编码方式,以哈夫曼树—即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。哈弗曼编码使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。哈夫曼编码的应用很广泛,利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。树中从根到每个叶子都有一条路径,对路径上的各分支约定:指向左子树的分支表示“0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为和各个叶子对应的字符的编码,这就是哈夫曼编码。哈弗曼译码输入字符串可以把它编译成二进制代码,输入二进制代码时可以编译成字符串。 第二章设计要求 对输入的一串电文字符实现哈夫曼编码,再对哈夫曼编码生成的代码串进行译码,输出电文字符串。通常我们把数据压缩的过程称为编码,解压缩的过程称为解码。电报通信是传递文字的二进制码形式的字符串。但在信息传递时,总希望总长度能尽可能短,即采用最短码。假设每种字符在电文中出现的次数为Wi,编码长度为Li,电文中有n种字符,则电文编码总长度为∑WiLi。若将此对应到二叉树上,Wi为叶结点的权,Li为根结点到叶结点的路径长度。那么,∑WiLi 恰好为二叉树上带权路径长度。因此,设计电文总长最短的二进制前缀编码,就是以n种字符出现的频率作权,构造一棵哈夫曼树,此构造过程称为哈夫曼编码。设计实现的功能: (1) 哈夫曼树的建立; (2) 哈夫曼编码的生成; (3) 编码文件的译码。

利用哈夫曼编码实现压缩和解压缩

利用哈夫曼编码实现压缩和解压缩 1.问题描述 利用哈夫曼编码,实现压缩和解压缩的数据元素具有如下形式: 结点: weight:存储结点的权值 parent:是结点双亲在向量中的下标 lchild:结点的左儿子向量下标 rchild:结点右儿子向量下标 bits:位串,存放编码 ch:字符 start:编码在位串中的起始位置 文件操作记录: b:记录字符在数组中的位置 count:字符出现频率(权值) lch、rch、parent:定义哈夫曼树指针变量 bits[256]:定义存储哈夫曼编码的数组 2.功能需求 对于给定的一组字符,可以根据其权值进行哈夫曼编码,并能输出对应的哈夫曼树和哈夫曼编码;实现哈夫曼解码。能够分析文件,统计文件中出现的字符,再对文件进行编码,实现文件的压缩和解压缩,能够对于文件的压缩,比例进行统计,能够打印文件。 3.实现要点 (1)构造哈弗曼树过程中,首先将初始森林的各根结点的双亲和左、右儿子指针置-1;叶子在向量T的前n个分量中,构成初始森林的n个结点;对森林中的树进行n次合并,

并产生n-1个新结点,依次放入向量T的第i个分量中。 (2)编码过程中,从叶子T[i]出发,利用双亲的指针找到双亲T[p];再根据T[p]的孩子指针可以知道T[i]是T[p]的左儿子还是右儿子,若是左儿子,则生成代码0,否则生成代码1; (3)在文件压缩和解压过程中,主要参考网上资料,每个步骤都基本理解,并注上了详细解析。 4.函数定义 功能:输入权重,构造一棵哈弗曼树 void huffman(hftree T) { if(n<1 || n > m)return; int i,j,p1,p2; float small1,small2; //初始化 cout<<"请输入叶子权重(5个):"<

哈夫曼编码实验报告

中南大学数据结构课程 姓名:刘阳 班级:信息0703 学号:0903070312 实验时间: 08.11.14 指导老师:赵颖

一、实验内容 根据输入的n 个带权结点,构造出哈夫曼树,并且把构造结果输出到屏幕。 二、实验说明 哈夫曼数,也称最优二叉树,是指对于一组带有确定权值的叶结点,构造的具有最小带权路径长度的二叉树。 设二叉树具有n 个带权值的叶结点,那么从根结点到各个叶结点的路径长度与相应结点权值的乘积之和叫做二叉树的带权路径长度WPL ,记作: WPL=k n k k L W *∑=1。在给定一组具有确定权值的叶结点,可以构造出不同的带权二 叉树。根据哈夫曼树的定义,一棵二叉树要使其WPL 值最小,必须使权值越大的叶结点越靠近根结点,而权值越小的叶结点越远离根结点。 在数据通讯中,经常需要将传送的文字转换成由二进制字符0,1组成的二进制串,我们称之为编码。例如,假设要传送的电文为ABACCDA ,电文中只含有A ,B ,C ,D 四种字符,若这四种字符采用下表所示的编码,则电文的代码为000010000100100111 000,长度为21。 在传送电文时,我们总是希望传送时间尽可能短,这就要求电文代码尽可能短。如果在编码时考虑字符出现的频率,让出现频率高的字符采用尽可能短的编码,出现频率低的字符采用稍长的编码,构造一种不等长编码,则电文的代码就可能更短。并且在建立不等长编码时,必须使任何一个字符的编码都不是另一个字符编码的前缀,以避免反译成原文时,编码出现多义性。 在哈夫曼编码树中,树的带权路径长度的含义是各个字符的码长与其出现次数的乘积之和,也就是电文的代码总长,所以采用哈夫曼树构造的编码是一种能使电文代码总长最短的不等长编码。 采用哈夫曼树进行编码,也不会产生上述二义性问题。因为,在哈夫曼树中,每个字符结点都是叶结点,它们不可能在根结点到其它字符结点的路径上,所以一个字符的哈夫曼编码不可能是另一个字符的哈夫曼编码的前缀,从而保证了译码的非二义性。

数据结构实验报告哈夫曼树

数据结构实验报告实验题目: Huffman编码与解码 姓名: 学号: 院系:

实验名称: Huffman编码与解码实验 问题描述: 本实验需要以菜单形式完成以下功能: 1、输入电文串 2、统计电文串中各个字符及其出现的次数 3、构造哈弗曼树 4、进行哈弗曼编码 5、将电文翻译成比特流并打印出来 6、将比特流还原成电文 数据结构的描述: 逻辑结构: 本实验可用二叉树实现,其逻辑结构为一对二的形式,即一个结点对应两个结点。在实验过程中我们也应用到了栈的概念。 存储结构: 使用结构体来对数据进行存储: typedef struct { int weight; int parent,lc,rc; }HTNode,*HuffmanTree; typedef struct LNode { char *elem; int stacksize; int top; }SqStack; 在main函数里面定义一个哈弗曼树并实现上述各种功能。 程序结构的描述: 本次实验一共构造了10个函数: 1.void HuffTree(HuffmanTree &HT,int n[],int mun); 此函数根据给定的mun个权值构建哈弗曼树,n[]用于存放num个权值。 2、void Select(HuffmanTree &HT,int n,int i,int &s1,int &s2);

此函数用于在HT[1,i-1]中选择parent为0且weight为最小的两个结点,其下标分别为s1,s2、 3.void HuffmanCoding(HuffmanTree HT,char **&HC,int n); 此函数从哈弗曼树HT上求得n 个叶子结点的哈弗曼编码并存入数组HC中。 4.void Coding(HuffmanTree HT,char **HC,int root,SqStack &S); 此函数用于哈弗曼编码,先序遍历哈弗曼树HT,求得每个叶子结点的编码字符串,存入数组HC,S为一个顺序栈,用来记录遍历路径,root就是哈弗曼数组HT中根结点的位置下标。 5.void InitStack(SqStack &S); 此函数用于初始化一个栈。 6.void Pop(SqStack &S,char e); 此函数为出栈操作。 7.void Push(SqStack &S,char e); 此函数为进栈操作。 8.int StackLength(SqStack S); 此函数用于求栈长,返回一个int型的值。 9.int Find(char a,char s[],int num); 此函数用于查找字符a在电文串中的位置。 10.int Recover(HuffmanTree HT,char **HC,char string[],char a[],char b[],int n); 此函数用于将比特流还原成电文。 调试分析: 输入任意一个字符串,如输入welcometoustc:运行结果如下:

霍夫曼树实验报告

实验二二叉树的遍历及霍夫曼编码 班级:计科1101班 学号:0909101605 姓名:杜茂鹏 2013年5月22日

一、实验目的 掌握二叉树的建立及遍历操作,霍夫曼编码基本操作及存储结构表示 二、实验内容 1. 系统要求包含以下功能 1)初始化:从终端读入字符集大小n,以及n个字符和n个权值(或者读入字符集和频度数据文件),建立哈夫曼树,并将哈夫曼树存入到文件HfmTree 中。 2)编码:利用已建好的哈夫曼树(如果不在内存中,则从文件中读入),从文件ToBeTran中读入原文,对原文进行编码,将编码后的结果存入文件CodeFile 中。 3)译码:利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。 4)打印:打印输出哈夫曼树,显示ToBeTran, TextFile和CodeFile文件的内容。 三、实验要求 1.在上机前写出全部源程序; 2.能在机器上正确运行程序; 3.用户界面友好。 四、概要设计 1)首先动态分配数组存储霍夫曼树及存储霍夫曼编码表,然后从终端或文件读入霍夫曼树的字符变量及其频度,初始化建立霍夫曼树并将其写入文件HfmTree.txt中。 2)从指定的文件succe.txt中读入原文,利用已经编好的霍夫曼树对其编码,将编码结果写入文件Coding.txt保存。 3)利用已建好的哈夫曼树将文件Coding.txt中的代码进行译码,结果存入文件decoding.txt中。

五、测试数据: 2.原文内容“THIS IS MY PROGRAM” 六、详细设计 实验内容(原理、操作步骤、程序代码) //建立霍夫曼树,对原文进行编码、译码 #include #include #include #include typedef struct tree { char ch; int weight;//权值 int parent,lchild,rchild; }HTNode,*HuffmanTree;//动态分配数组存储霍夫曼树typedef char **HuffmanCode;//动态分配数组存储霍夫曼编码表void Select(HuffmanTree &HT,int* s1,int* s2,int n) { int j; int min1=10000; for(j=1;j<=n;j++) { if(HT[j].parent==0&&min1>HT[j].weight)

哈夫曼树实验报告

哈夫曼树实验报告 Company number:【0089WT-8898YT-W8CCB-BUUT-202108】

计算机科学与技术学院数据结构实验报告 班级 2014级计算机1班学号姓名张建华成绩 实验项目简单哈夫曼编/译码的设计与实现实验日期一、实验目的 本实验的目的是进一步理解哈夫曼树的逻辑结构和存储结构,进一步提高使用理论知识指导解决实际问题的能力。 二、实验问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码,此实验即设计这样的一个简单编/码系统。系统应该具有如下的几个功能: 1、接收原始数据。 从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件中。 2、编码。 利用已建好的哈夫曼树(如不在内存,则从文件中读入),对文件中的正文进行编码,然后将结果存入文件中。 3、译码。 利用已建好的哈夫曼树将文件中的代码进行译码,结果存入文件中。 4、打印编码规则。 即字符与编码的一一对应关系。 5、打印哈夫曼树, 将已在内存中的哈夫曼树以直观的方式显示在终端上。 三、实验步骤 1、实验问题分析 1、构造哈夫曼树时使用静态链表作为哈夫曼树的存储。 在构造哈夫曼树时,设计一个结构体数组HuffNode保存哈夫曼树中各结点的信息,根据二叉树的性质可知,具有n个叶子结点的哈夫曼树共有2n-1个结点,所以数组HuffNode的大小设置为2n-1,描述结点的数据类型为: Typedef strcut { Int weight;/*结点权值*/ Int parent; Int lchild; Int rchild; }HNodeType; 2、求哈夫曼编码时使用一维结构数组HuffCode作为哈夫曼编码信息的存储。 求哈夫曼编码,实质上就是在已建立的哈夫曼树中,从叶子结点开始,沿结点的双亲链域回退到根结点,没回退一步,就走过了哈夫曼树的一个分支,从而得到一位哈夫曼码值,由于一个字符的哈夫曼编码是从根结点到相应叶子结点所经过的路

哈夫曼树 实验报告

计算机科学与技术学院数据结构实验报告 班级2014级计算机1班学号20144138021 姓名张建华成绩 实验项目简单哈夫曼编/译码的设计与实现实验日期2016.1.5 一、实验目的 本实验的目的是进一步理解哈夫曼树的逻辑结构和存储结构,进一步提高使用理论知识指导解决实际问题的能力。 二、实验问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码,此实验即设计这样的一个简单编/码系统。系统应该具有如下的几个功能: 1、接收原始数据。 从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmtree.dat中。 2、编码。 利用已建好的哈夫曼树(如不在内存,则从文件hfmtree.dat中读入),对文件中的正文进行编码,然后将结果存入文件codefile.dat中。 3、译码。 利用已建好的哈夫曼树将文件codefile.dat中的代码进行译码,结果存入文件textfile.dat中。 4、打印编码规则。 即字符与编码的一一对应关系。 5、打印哈夫曼树, 将已在内存中的哈夫曼树以直观的方式显示在终端上。 三、实验步骤 1、实验问题分析 1、构造哈夫曼树时使用静态链表作为哈夫曼树的存储。 在构造哈夫曼树时,设计一个结构体数组HuffNode保存哈夫曼树中各结点的信息,根据二叉树的性质可知,具有n个叶子结点的哈夫曼树共有2n-1个结点,所以数组HuffNode的大小设置为2n-1,描述结点的数据类型为: Typedef strcut { Int weight;/*结点权值*/ Int parent; Int lchild; Int rchild; }HNodeType; 2、求哈夫曼编码时使用一维结构数组HuffCode作为哈夫曼编码信息的存储。 求哈夫曼编码,实质上就是在已建立的哈夫曼树中,从叶子结点开始,沿结点的双亲链域回退到根结点,没回退一步,就走过了哈夫曼树的一个分支,从而得到一位哈夫曼码值,由于一个字符的哈夫曼编码是从根结点到相应叶子结点所经过的路径上各分支所组成的0、1序列,因此先得到的分支代码为所求编码的低位码,后得到的分支代码位所求编码的高位码,所以设计如下数据类型:

哈夫曼树的实验报告1

一、需求分析 1、本演示程序实现Haffman编/译码器的作用,目的是为信息收发站提供一个编/译系统, 从而使信息收发站利用Haffman编码进行通讯,力求达到提高信道利用率,缩短时间,降低成本等目标。系统要实现的两个基本功能就是:①对需要传送的数据预先编码; ②对从接收端接收的数据进行译码; 2、本演示程序需要在终端上读入n个字符(字符型)及其权值(整形),用于建立Huffman 树,存储在文件hfmanTree.txt中;如果用户觉得不够清晰还可以打印以凹入表形式显示的Huffman树; 3、本演示程序根据建好的Huffman树,对文件的文本进行编码,结果存入文件CodeFile 中;然后利用建好的Huffman树将文件CodeFile中的代码进行译码,结果存入文件TextFile中;最后在屏幕上显示代码(每行50个),同时显示对CodeFile中代码翻译后的结果; 4、本演示程序将综合使用C++和C语言; 5、测试数据: (1)教材例6-2中数据:8个字符,概率分别是0.05,0.29,0.07,0.08,0.14,0.23,0.03, 0.11,可将其的权值看为5,29,7,8,14,23,3,11 (2)用下表给出的字符集和频度的实际统计数据建立Haffman树,并实现以下报文的编码和 一、概要设计 1、设定哈夫曼树的抽象数据类型定义 ADT Huffmantree{ 数据对象:D={a i| a i∈Charset,i=1,2,3,……n,n≥0} 数据关系:R1={< a i-1, a i >| a i-1, a i∈D, i=2,3,……n} 基本操作: Initialization(&HT,&HC,w,n,ch) 操作结果:根据n个字符及其它们的权值w[i],建立Huffman树HT,用字符数组ch[i]作为中间存储变量,最后字符编码存到HC中; Encodeing(n) 操作结果:根据建好的Huffman树,对文件进行编码,编码结果存入到文件CodeFile 中 Decodeing(HT,n) 操作结果:根据已经编译好的包含n个字符的Huffman树HT,将文件的代码进行翻译,结果存入文件TextFile中 } ADT Huffmantree

英文文件的压缩和解压缩

合肥学院 计算机科学与技术系 课程设计报告 2009~2010学年第二学期 课程数据结构与算法 课程设计名称英文文件的压缩和解压缩 学生姓名 学号 专业班级 指导教师李红沈亦军

2010 年 6 月 题目:(采用哈夫曼编码思想实现文件的压缩和解压缩功能,并提供压缩前后的占用空间之比)(1)压缩原文件的规模应不小于5K。(2)提供解压缩后文件与原文件的相同性比较功能。 一、问题分析和任务定义。 1.1问题分析 本实验是利用哈夫曼编码思想,设计对一个文本文件中的字符进行哈夫曼编码,生成编码文件(压缩文件);反过来,可将一个压缩文件译码还原为一个文本文件(.txt)。要解决以上问题,需从以下几个方面着手: (1)如何读入待压缩的文本文件,统计文本文件中各字符的频数及出现的字符个数; (2)如何构造huffman树,进行huffman编码,生成压缩文件(后缀名.txt (3)如何读入待解压的压缩文件名,并利用相应的哈夫曼树及压缩文件中的二进制码将编码序列译码,对文件进行解压,生成解压文件(后缀名为.txt); (4)如何提供压缩前后的占用空间之比 1.2输入、输出数据的形式、值的范围,算法(程序)所能达到的功能 本实验的数据主要是以字符型为主,还有一些自定义的整形和浮点型变量,该实验室对文件进行压缩和解压(被压缩文件容量要求大于>5KB),通过该算法程序可以大致上满足实验所要求的功能,即压缩原文件的规模不小于5KB,提供了压缩后的文件与原文件的压缩比例,也即提供了性能比较功能 1.3 测试用的数据 本实验的数据是通过读入一个名为huffman.txt的文本文档,文档中内容为字符型数据。 所测试的部分数据:

哈夫曼树实验报告

数据结构实验报告 实验名称:实验三哈夫曼树 学生姓名: 班级: 班内序号: 学号: 日期: 程序分析: 存储结构:二叉树 程序流程: template class BiTree { public: ) 1.初始化链表的头结点

2.获得输入字符串的第一个字符,并将其插入到链表尾部,n=1(n记录的是链 表中字符的个数) 3.从字符串第2个字符开始,逐个取出字符串中的字符 将当前取出的字符与链表中已经存在的字符逐个比较,如果当前取出的 字符与链表中已经存在的某个字符相同,则链表中该字符的权值加1。 如果当前取出的字符与链表中已经存在的字符都不相同,则将其加入到 链表尾部,同时n++ =n(tSize记录链表中字符总数,即哈夫曼树中叶子节点总数) 5.创建哈夫曼树 6.销毁链表 源代码: void HuffmanTree::Init(string Input) { Node *front=new Node; 建哈夫曼树(void HuffmanTree::CreateCodeTable(Node *p)) 算法伪代码: 1.创建一个长度为2*tSize-1的三叉链表 2.将存储字符及其权值的链表中的字符逐个写入三叉链表的前tSize个结点 的data域,并将对应结点的孩子域和双亲域赋为空 3.从三叉链表的第tSize个结点开始,i=tSize 3.1从存储字符及其权值的链表中取出两个权值最小的结点x,y,记录其 下标x,y。 3.2将下标为x和y的哈夫曼树的结点的双亲设置为第i个结点 3.3将下标为x的结点设置为i结点的左孩子,将下标为y的结点设置为 i结点的右孩子,i结点的权值为x结点的权值加上y结点的权值,i 结点的双亲设置为空 4. 根据哈夫曼树创建编码表

哈夫曼树与文件解压压缩C言代码

1.问题描述 哈弗曼树的编码与译码 —功能:实现对任何类型文件的压缩与解码 —输入:源文件,压缩文件 —输出:解码正确性判定,统计压缩率、编码与解码速度 —要求:使用边编码边统计符号概率的方法(自适应Huffman编码)和事先统计概率的方法(静态Huffman编码) 2.1程序清单 程序书签: 1.main函数 2.压缩函数 3.select函数 4.encode函数 5.解压函数 #include #include #include #include #include struct node{

long weight; //权值 unsigned char ch;//字符 int parent,lchild,rchild; char code[256];//编码的位数最多为256位int CodeLength;//编码长度 }hfmnode[512]; void compress(); void uncompress(); //主函数 void main() { int choice; printf("请选择1~3:\n"); printf("1.压缩文件\n"); printf("2.解压文件\n"); printf("3.退出!\n"); scanf("%d",&choice); if(choice==1)compress(); else if(choice==2)uncompress(); else if(choice==3)return; else printf("输入错误!"); }

huffman编码译码实现文件的压缩与解压.

数据结构 课程设计 题目名称:huffman编码与解码实现文件的压缩与解压专业年级: 组长: 小组成员: 指导教师: 二〇一二年十二月二十六日

目录 一、目标任务与问题分析 (2) 1.1目标任务 (2) 1.2问题分析 (2) 二、算法分析 (2) 2.1构造huffman树 (2) 2.1.1 字符的统计 (2) 2.1.2 huffman树节点的设计 (2) 2.2构造huffman编码 (3) 2.2.1 huffman编码的设计 (3) 2.3 压缩文件与解压文件的实现 (3) 三、执行效果 (4) 3.1界面 (4) 3.2每个字符的编码 (4) 3.3操作部分 (5) 3.4文件效果 (6) 四、源程序 (7) 五、参考文献 (16)

huffman编码与解码实现文件的压缩与解压 一、目标任务与问题分析 1.1目标任务 采用huffman编码思想实现文件的压缩和解压功能,可以将任意文件压缩,压缩后也可以解压出来。这样即节约了存储空间,也不会破坏文件的完整性。 1.2问题分析 本问题首先应该是利用哈夫曼思想,对需要压缩的文件中的个字符进行频率统计,为了能对任意的文件进行处理,应该所有的文件以二进制的方式进行处理,即对文件(不管包含的是字母还是汉字)采取一个个的字节处理,然后根据统计的频率结果构造哈夫曼树,然后对每个字符进行哈夫曼编码,然后逐一对被压缩的文件的每个字符构建的新的哈夫曼编码存入新的文件中即得到的压缩文件。解压过程则利用相应的哈夫曼树及压缩文件中的二进制码将编码序列译码,对文件进行解压,得到解压文件。 二、算法分析 2.1构造huffman树 要利用哈夫曼编码对文本文件进行压缩,首先必须知道期字符相应的哈夫曼编码。为了得到文件中字符的频率,一般的做法是扫描整个文本进行统计,编写程序统计文件中各个字符出现的频率。由于一个字符的范围在[0-255]之间,即共256个状态,所以可以直接用256个哈夫曼树节点即数组(后面有节点的定义)空间来存储整个文件的信息,节点中包括对应字符信息,其中包括频率。 2.1.1 字符的统计 用结构体huffchar来存放文件字符的信息。其中有文件中不同字符出现的种类Count、字符data。 struct huffchar{ //存放读入字符的类; int Count;//字符出现的个数; char data;//字符; }; 函数实现: bool char_judge(char c)//判断字符出现的函数; void char_add(char c)//添加新出现的字符; void read_file_count() //文件的读取 2.1.2 huffman树节点的设计 用结构体huff_tree来存储结点信息,其中有成员频率weight、父亲节点parent、左儿子节点lchild、右儿子节点rchild。

Huffman编码对英文文本的压缩和解压缩

Huffman编码对英文文本的压缩和解压缩中国地质大学计算机学院信息安全专业 信息论实验报告 #include #include #include struct head { unsigned char b; //记录字符在数组中的位置 long count; //字符出现频率(权值) long parent,lch,rch; //定义哈夫曼树指针变量char bits[256]; //定义存储哈夫曼编码的数组}header[512],tmp; void compress() { char filename[255],outputfile[255],buf[512]; unsigned char c; long n,m,i,j,f; //作计数或暂时存储数据用 long min1,pt1,flength=0,length1,length2; //记录最小结点、文件长度 double div; //计算压缩比用 FILE *ifp,*ofp; //分别为输入、输出文件指针printf("\t请您输入需要压缩的文件(需要路径):");

gets(filename); ifp=fopen(filename,"rb"); if(ifp==NULL){ printf("\n\t文件打开失败!\n "); system("pause"); return; } printf("\t请您输入压缩后的文件名(如无路径则默认为桌面文件):"); gets(outputfile); ofp=fopen(outputfile,"wb"); if(ofp==NULL){ printf("\n\t压缩文件失败!\n "); system("pause"); return; } flength=0; while(!feof(ifp)){ fread(&c,1,1,ifp); header[c].count++; //字符重复出现频率+1 flength++; //字符出现原文件长度+1 }

哈夫曼树及其操作-数据结构实验报告(2)

电子科技大学 实验报告 课程名称:数据结构与算法 学生姓名:陈*浩 学号:************* 点名序号: *** 指导教师:钱** 实验地点:基础实验大楼 实验时间: 2014-2015-2学期 信息与软件工程学院

实验报告(二) 学生姓名:陈**浩学号:*************指导教师:钱** 实验地点:科研教学楼A508实验时间:一、实验室名称:软件实验室 二、实验项目名称:数据结构与算法—树 三、实验学时:4 四、实验原理: 霍夫曼编码(Huffman Coding)是一种编码方式,是一种用于无损数据压缩的熵编码(权编码)算法。1952年,David A. Huffman在麻省理工攻读博士时所发明的。 在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。 例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。 霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。 可以证明霍夫曼树的WPL是最小的。

数据结构实验三哈夫曼树实验报告

题目:哈夫曼编/译码器 一、题目要求: 写一个哈夫曼码的编/译码系统,要求能对要传输的报文进行编码和解码。构造哈夫曼树时,权值小的放左子树,权值大的放右子树,编码时右子树编码为1,左子树编码为0. 二、概要设计: 数据结构: typedef struct { int bit[MAXBIT]; int start; } HCodeType; /* 编码结构体 */ typedef struct { int weight; int parent; int lchild; int rchild; char value; } HNode; /* 结点结构体 */ 函数: void DEMONHuffmanTree (HNode HuffNode[MAXNODE], int n) 作用:构造一个哈夫曼树,并循环构建 int main () 作用:运用已经构建好的哈弗曼树,进行节点的处理,达到成功解码编译 三、详细设计: 哈夫曼树的建立: void DEMONHuffmanTree (HNode HuffNode[MAXNODE], int n) { int i = 0, j, m1, m2, x1, x2; char x; /* 初始化存放哈夫曼树数组 HuffNode[] 中的结点 */ while (i

HuffNode[i].rchild =-1; scanf("%c",&x); scanf("%c",&HuffNode[i].value); //实际值,可根据情况替换为字母 i++; } /* 输入 n 个叶子结点的权值 */ scanf("%c",&x); for(i=0;i

用哈夫曼树实现图像压缩

———用哈弗曼算法实现图像压缩 姓名:黄函 学号:2011221104210146 班级:11计科3班 算法设计课程论文

1.数字图像的冗余表现为以下几种形式:空间冗余、时间冗余、视觉冗余、信息熵冗余、结构冗余和知识冗余。 (1)空间冗余:图像内部相邻像素之间存在较强的相关性所造成的冗余。 (2)时间冗余:视频图像序列中的不同帧之间的相关性所造成的冗余。 (3)视觉冗余:是指人眼不能感知或不敏感的那部分图像信息。 (4)信息熵冗余:也称编码冗余,如果图像中平均每个像素使用的比特数大于该图像的信息熵,则图像中存在冗余,这种冗余称为信息熵冗余。 (5)结构冗余:是指图像中存在很强的纹理结构或自相似性。 (6)知识冗余:是指有些图像还包含与某些先验知识有关的信息。 2.无损压缩很常见的例子是磁盘文件的压缩。有损压缩的实例是图像和声音的压缩。 3.图像压缩的目的就是在给定位速或者压缩比下实现最好的图像质量。但是,还有一些其它的图像压缩机制的重要特性: 可扩展编码:又称渐进编码、嵌入式位流,通常表示操作位流和文件产生的质量下降(没有解压缩和再压缩)。尽管具有不同的特性,在无损编码中也有可扩展编码,它通常是使用粗糙到精细像素扫描的格式。尤其是在下载时预览图像(如浏览器中)或者提供不同的图像质量访问时(如在数据库中)可扩展编码非常有用,有几种不同类型的可扩展性: 质量渐进:又称层渐进,位流渐进更新重建的图像。 分辨率渐进:首先在低分辨率编码图像,然后编码与高分辨率之间的差别。 成分渐进:首先编码灰度数据,然后编码彩色数据。 感兴趣区域编码:图像某些部分的编码质量要高于其它部分,这种方法可以与可扩展编码组合在一起(首先编码这些部分,然后编码其它部分)。 元数据信息:压缩数据可以包含关于图像的信息用来分类、查询或者浏览图像。这些信息可以包括颜色、纹理统计信息、小预览图像以及作者和版权信息。 压缩方法的质量经常使用峰值信噪比来衡量,峰值信噪比用来表示图象有损压缩带来的噪声。但是,观察者的主观判断也认为是一个重要的、或许是最重要的衡量标准。 4.以哈弗曼算法为实例了解图像压缩算法 Huffman码是一种变长码,其基本思想是:先统计图像(已经数字化)中各灰度出现的概率,出现概率较大的赋以较短的码字,而出现概率较小的则赋以较长的码字。我们可以用下面的框图来表示Huffman编码的过程: 在整个编码过程中,统计图像各灰度级出现的概率和编码这两步都很简单,关键的是Huffman树的构造。不但编码的时候需要用到这颗树,解码的时候也必须有这颗树才能完成解码工作,因此,Huffman树还得完整的传输到解码端。 Huffman树的构造可以按照下面图2的流程图来完成。首先对统计出来的概率从小到大进行排序,然后将最小的两个概率相加;到这儿的时候,先把已经加过的两个概率作为树的两个节点,并把他们从概率队列中删除;然后把相加所得的新概率加入到队列中,对这个新队列进行排序。如此反复,直到最后两个概率相加为1的时候停止。这样,Huffman树就建立起来了。 5.哈夫曼图像压缩算法性能评价 我们主要从三方面[ 2 ]来评价Huffman的性能: (1)压缩比的大小; (2)恢复效果的好坏,也就是能否尽可能的恢复原始数据; (3)算法的简单易用性以及编、解码的速度。

哈夫曼树实验报告(付原C语言程序)

哈夫曼树实验报告 需求分析: 从终端读入一串字符,利用建立好的哈夫曼树对其进行编码,储存到文件当中去,然后从文件读入哈夫曼编码,针对每个字母对其进行译码,翻译为原来的信息。 二、概要设计 程序分为以下几个模块: 1、从终端读入字符集大小,n个字符和n个权值,建立哈夫曼树,写入文件hfmTree中去。 2、对hfmTree进行编码,建立hfm编码表。 3、从文件ToTran读入信息,根据hfm编码表对其进行hfm编码,将编码后的信息写入文件Codefile 中去 4、对Codefile文件反向译码,结果储存在Textfile中去。 5、将建立的hfmTree打印在终端上,并储存于相应的Treeprint文件中去。 抽象的数据定义如下: 哈夫曼树结构 typedef struct //定义哈夫曼树的结构 { int weight; //权值 int parent; //双亲 int lchild; //左孩子 int rchild; //右孩子 }htnode,huffmantree[M+1]; 建立哈夫曼树 void crthuffmantree(huffmantree ht,int w[],int n) //初始化哈夫曼树 { int i,s1,s2,m; for(i=1;i<=n;i++) { ht[i].weight=w[i]; ht[i].parent=0; ht[i].lchild=0; ht[i].rchild=0; } m=2*n-1; for(i=n+1;i<=m;i++) { ht[i].weight=0; ht[i].parent=0; ht[i].lchild=0; ht[i].rchild=0; } for(i=n+1;i<=m;i++) { select(ht,i-1,&s1,&s2); ht[i].weight=ht[s1].weight+ht[s2].weight; ht[s1].parent=i;

哈夫曼编码实验报告

哈夫曼编码: 哈夫曼编码,又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码。 发展历史: 1951年,哈夫曼和他在MIT信息论的同学需要选择是完成学期报告还是期末考试。导师Robert M. Fano给他们的学期报告的题目是,寻找最有效的二进制编码。由于无法证明哪个已有编码是最有效的,哈夫曼放弃对已有编码的研究,转向新的探索,最终发现了基于有序频率二叉树编码的想法,并很快证明了这个方法是最有效的。由于这个算法,学生终于青出于蓝,超过了他那曾经和信息论创立者香农共同研究过类似编码的导师。 1952年,David A. Huffman在麻省理工攻读博士时发表了《一种构建极小多余编码的方法》(A Method for the Construction of Minimum-Redundancy Codes)一文,它一般就叫做Huffman编码。 Huffman在1952年根据香农(Shannon)在1948年和范若(Fano)在1949年阐述的这种编码思想提出了一种不定长编码的方法,也称霍夫曼(Huffman)编码。霍夫曼编码的基本方法是先对图像数据扫描一遍,计算出各种像素出现的概率,按概率的大小指定不同长度的唯一码字,由此得到一张该图像的霍夫曼码表。编码后的

图像数据记录的是每个像素的码字,而码字与实际像素值的对应关系记录在码表中。 赫夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就称Huffman 编码。下面引证一个定理,该定理保证了按字符出现概率分配码长,可使平均码长最短。

哈夫曼树解压与压缩

哈夫曼树的压缩与解压 1.算法简要描述 1.哈夫曼算法 1.哈弗曼算法是根据给定的n个权值{w1,w2,w3.......wn},构造由n棵 二叉树构成的深林F={T1,T2,。。。。Tn},其中每个二叉树Ti分别都是只 含有一个权值wi的根结点,其左右子树为空(i=1,,,,,,2)。 2.在深林F中选取其根结点的权值最小的两棵二叉树,分别作其左右子树 构造一颗新的二叉树,并置这棵新的二叉树根结点的权值为其左右子树 的根结点之和。 3.从F中删去这两棵二叉树,同时刚新生成的二叉树加入到深林F中。 4.重复2,3,步骤,直至深林F中只含有一颗二叉树为止。 2.哈夫曼树的实现 函数String EnCode(Char Type ch):表示哈夫曼树已存在,返回字符ch的编码。 函数LinkListUnCode(String strCode):表示对哈夫曼树进行译码,返回编码前的字符序列。根据算法可以看出,在具有n个结点权值的哈夫曼树的构造过程中,每次都是从F中删去两棵树,增加一棵树,即每次结束后减少一棵树,经过n-1次处理后,F中就只剩下一棵树了。另外,每次合并都要产生一个新的结点,合并n-1次后共产生了n-1个新结点,并且这n-1个新节点都是具有左右子树的分支结点。则最终得到的哈夫曼树中共有2n-1个结点,并且其中没有度为1的分支结点,最后一次产生的新结点就是哈夫曼树的根结点。

源代码中创建了一个哈夫曼树结点类,其中有数据成员weight,parent,leftChild,rightChild分别代表了权值,双亲,左孩子,右孩子。 在哈夫曼树类中有数据成员*nodes,*LeafChars,*LeafCharCodes,curPos,num,分别用来存储结点信息,叶结点字符信息,叶结点字符编码信息,译码时从根结点到叶结点路径的当前结点,叶结点个数。哈夫曼树类中含有多个函数,有构造函数,析构函数等。由函数HuffmanTree(CharType ch[],WeightType w[],int n)来构造由字符,权值,和字符个数构造哈夫曼树,在根据哈夫曼算法很容易实现哈夫曼类的函数以及构造函数。在在算法中,求叶结点字符的编码时,需要从叶结点出发走一条从高叶结点到根结点的路径,而编码却是从根结点出发到叶结点的路径,由左分支为编码0,右分支为编码1,得到的编码,因此从叶结点出发到根结点的路径得到的编码是实际编码的逆序,并且编码长度不确定,又由于可以再线性链表中构造串,因此将编码的信息储存在一个线性立案标准,每得到一位编码都将其插入在线性链表的最前面。 在求某个字符的编码是由函数EnCode(CharType ch)来求,返回字符编码。在进行译码时,用一个线性链表存储字符序列,由函数Decode(String strCode)来求,对编码串strCode进行译码,返回编码前的字符序列。函数Compress()用哈夫曼编码压缩文件。函数Decompress()解压缩用哈夫曼编码压缩的文件。 在主函数中有两个选项,一个是选择编码压缩,一个是解压。在函数中使用了文件输入输出流,我们可以选择要压缩的文件名输入,在选出压缩文件保存的地方和文件类型,将压缩所得到的文件存储在另一个文件中,解压也是如此。

相关文档
最新文档