武汉理工大学数据结构与算法综合实验哈夫曼树.docx
《武汉理工大学数据结构与算法综合实验哈夫曼树.docx》由会员分享,可在线阅读,更多相关《武汉理工大学数据结构与算法综合实验哈夫曼树.docx(12页珍藏版)》请在冰豆网上搜索。
![武汉理工大学数据结构与算法综合实验哈夫曼树.docx](https://file1.bdocx.com/fileroot1/2023-2/8/a28d3cc8-3e0f-44c7-a242-7ad529ece08b/a28d3cc8-3e0f-44c7-a242-7ad529ece08b1.gif)
武汉理工大学数据结构与算法综合实验哈夫曼树
学生学号
Xxx
实验课成绩
学生实验报告书
实验课程名称
数据结构与算法综合实验
开课学院
计算机科学与技术学院
指导教师姓名
xxx
学生姓名
xxx
学生专业班级
xxxx
2015
--
2016
学年
第
2
学期
实验课程名称:
数据结构与算法综合实验
实验项目名称
二叉树与赫夫曼图片压缩
报告成绩
实验者
xx
专业班级
xxx
组别
同组者
完成日期
2016年5月2日
第一部分:
实验分析与设计(可加页)
一、实验目的和要求
1.目的
掌握树的存储结构
掌握二叉树的三种遍历方法
掌握Huffman树、Huffman编码等知识和应用
使用C++、文件操作和Huffman算法实现“图片压缩程序”专题编程。
2.要求
针对一幅BMP格式的图片文件,统计256种不同字节的重复次数,以每
种字节重复次数作为权值,构造一颗有256个叶子节点的哈夫曼二叉树。
利用上述哈夫曼树产生的哈夫曼编码对图片文件进行压缩。
压缩后的文件与原图片文件同名,加上后缀.huf(保留原后缀),如pic.bmp
压缩后pic.bmp.huf
二、分析与设计
依据上述的实验目的与要求,可导出实现的二叉树与赫夫曼图片压缩软件的流程为:
①读取图片文件、统计权值
②生成Huffman树
③生成Huffman编码
④压缩图片文件
⑤保存压缩的文件
1.数据结构的设计
●记录统计256种不同字节的重复次数使用整型数组。
intweight[256]={0};
●二叉树的存储结构。
使用结构体存储节点,使用数组存储树的节点,使用静态二叉链表方式存储二叉树。
●Huffman编码存储结构
structHTNode
{
intweight;//权值
intparent;
intlchild;
intrchild;
charzifu;
stringbianma;
};
●压缩文件的算法的数据结构
为正确解压文件,除了要保存原文件长度外,还要保存原文件中256种字节重复的次数,即权值。
定义一个文件头,保存相关的信息:
structHEAD
{
chartype[4];
intlength;
intweight[256];
};
压缩文件时,定义一个内存缓冲区:
typedefchar*pBuffer
;//其大小视原文件压缩后的大小
2.核心算法设计
(1)生成Huffman树和Huffman编码的算法
voidSelect(HTNodehuffTree[],intm)
{
intmin,min2,i;
min=min2=1000;
for(i=0;iif(huffTree[i].parent==-1)
if(min>huffTree[i].weight)
{
min2=min;
min=huffTree[i].weight;
x2=x1;
x1=i;
}
elseif(min2>huffTree[i].weight)
{
min2=huffTree[i].weight;
x2=i;
}
}
voidcreatHuffman(inthuff[])
{
inti;
ints=256;
for(i=0;i<2*s-1;i++)
{
HuffmanTree[i].parent=-1;
HuffmanTree[i].lchild=-1;
HuffmanTree[i].rchild=-1;
}
for(inti1=0;i1
HuffmanTree[i1].weight=huff[i1];
for(intk=s;k<2*s-1;k++)
{
Select(HuffmanTree,k);
HuffmanTree[x1].parent=k;
HuffmanTree[x2].parent=k;
HuffmanTree[k].weight=HuffmanTree[x1].weight+HuffmanTree[x2].weight;
HuffmanTree[k].lchild=x1;
HuffmanTree[k].rchild=x2;
}
}
voidHuffmanCoding(HTNodehuffTree[],intn)
{
inti,j=0;
for(i=2*(n-1);i>n-1;i--)
{
huffTree[huffTree[i].lchild].bianma="0";
huffTree[huffTree[i].rchild].bianma="1";
}
for(i=0,j=0;j{
while(huffTree[i].parent!
=-1)
{
huffTree[j].bianma=huffTree[huffTree[i].parent].bianma+huffTree[j].bianma;
i=huffTree[i].parent;
}
i=j+1;
}
}
(2)压缩编码算法
structHEAD
{
chartype[4];
intlength;
intweight[256];
};
charStr2byte(constchar*pBinStr)
{
charb=0x00;
for(inti=0;i<8;i++)
{
b=b<<1;
if(pBinStr[i]=='1')
{
b=b|0x01;
}
}
returnb;
}
boolInitHead(constchar*pFilename,HEAD&sHead)
{
charch;
//初始化文件
strcpy(sHead.type,"HUF");
sHead.length=0;
for(inti=0;i<256;i++)
{
sHead.weight[i]=0;
}
ifstreamin;
in.open(pFilename,ios:
:
binary);
while(in.get(ch)){sHead.weight[(unsignedchar)ch]++;sHead.length++;}
cout<returntrue;
}
intEncode(constchar*pFilename,char*&pBuffer,constintnSize)
{
pBuffer=(char*)malloc(nSize*sizeof(char)+10);
if(!
pBuffer)
{
cout<<"开辟缓冲区失败"<}
charcd[256]={0};
intpos=0;
intch;
FILE*in=fopen(pFilename,"rb");
while((ch=fgetc(in))!
=EOF)
{
strcat(cd,HuffmanTree[ch].bianma.c_str());
while(strlen(cd)>=8)
{
//cout<pBuffer[pos++]=Str2byte(cd);//cout<for(inti=0;i<256-8;i++)
{
cd[i]=cd[i+8];
}
}
}
if(strlen(cd)>0)
{
pBuffer[pos++]=Str2byte(cd);
}
return1;
}
intWriteFile(constchar*pFilename,constHEADsHead,char*pBuffer,constintnSize)
{
//生成文件名
charfilename[256]={0};
strcpy(filename,pFilename);
inti;
for(i=strlen(filename);filename[i]!
='.';i--);
filename[i]='\0';
strcat(filename,".huf");
//以二进制流的形式打开文件
FILE*out=fopen(filename,"wb");
//写文件头
fwrite(&sHead,sizeof(HEAD),1,out);
//写压缩后的编码
fwrite(pBuffer,sizeof(char),nSize,out);
//关闭文件释放文件指针
fclose(out);
out=NULL;
cout<<"生成压缩文件"<intlen=sizeof(HEAD)+strlen(pFilename)+1+nSize;
returnlen;
}
intcompress(constchar*pFilename,intweight[256],constHEADsHead)
{
//计算缓冲区的大小
intnSize=0;
for(inti=0;i<256;i++)
{
nSize+=weight[i]*HuffmanTree[i].bianma.length();
}
nSize=(nSize%8)?
nSize/8+1:
nSize/8;
//cout<<"nSize"<char*pBuffer=NULL;
Encode(pFilename,pBuffer,nSize);
//if(pBuffer==NULL)//cout<<"wrong"<if(!
pBuffer)
{
return0;
}
intresult=WriteFile(pFilename,sHead,pBuffer,nSize);
returnresult;
}
3.测试用例设计
●使用一个文本文件作为压缩的例,观察其压缩比;
●通过屏幕截图形成一个BMP图片文件,观察其压缩比;
●在互联网上搜索下载任意格式的图片文件,观察其压缩比。
三、主要仪器设备及耗材
1.安装了Windows10或其它版本的Windows操作系统的PC机1台
2.PC机系统上安装了MicrosoftVisualStudio2010开发环境
第二部分:
实验过程和结果(可加页)
一、实现说明
在MicrosoftVisualStudio2010集成开发环境中新建一个Win32控制台应用程序工程HfmCompressConsole。
HfmCompressConsole工程中新建2组相关文件。
第1组是实现依据图片文件构建其Huffman编码的头文件Huffman.h和源程序文件Huffman.cpp。
第2组是实现图片文件压缩编码和写磁盘等功能的头文件Compress.h和源程序文件Compress.cpp。
Huffman.h存放与Huffman.cpp相关函数需要的数据类型的定义,函数原型的声明等。
Compress.h存放与Compress.cpp相关函数需要的数据类型的定义,函数原型的声明等。
最后新建一个main.cpp源文件,实现main函数按分析与设计中规定的流程调用Huffman.cpp和Compress.cpp的功能函数。
二、调试说明(调试手段、过程及结果分析)
调试主要内容为编写程序的语法正确性与否,程序逻辑的正确性与否。
调试手段主要采用了MicrosoftVisualStudio2010集成开发环境中“调试(D)”菜单中的调试方法或手段。
即:
F5:
启动调试;F11:
逐语句调试;F12:
逐过程调试;F9:
切换断点;ctrl+B:
新建断点等。
例如在统计图片文件中0-255取值的256个字节出现的次数函数中,设置断点并使用简单的文本文件进行测试,发现了“没有扫描完整个文件而是中途跳出”的问题。
通过断点出查看weight数组的值以及通过逐语句跳出的处定位错误所在之处。
找出问题的原因是以流的形式读入的字符定义问题,charch;ch=fgetc(in);
Weight[ch]++;字符变量ch在转换成int时出现了负数。
当将ch的定义修改Unsignedcharch;问题解决。
再例:
文件编码压缩Encode()函数会产生编码后的一个缓冲区char*pBuffer;写文件函数会使用它直接写磁盘文件。
调试过程中并没发现任何问题,就是不能成功地写后缀为.huf的文件。
在相关函数中设置断点,观察缓冲区的情况,且编写屏幕输出缓冲区数据的程序段,发现缓冲区是空的。
通过在Encode函数中以及WriteFile函数中做同样的跟踪调试,发现在Encode函数中建立的缓冲区数据并没有带出来,通过分析发现是缓冲区空间构建位置的问题,即不能放在Encode函数中。
三、软件测试(测试效果.界面、综合分析和结论)
1.测试效果.界面
2.综合分析和结论
试验在压缩txt文件的时候没有问题,可以通过编译生成可执行文件,但是在进行图片的压缩时出了问题,导致程序出错,所以我编写的哈夫曼树压缩文件不能正确压缩图片。
第三部分:
实验小结、收获与体会
通过这次试验,我对Huffman树的创建和Huffman编码的产生有了更深的理解,同时对于文件的压缩有了更进一步的认识也更加理解了数据结构在实际应用中的作用。
通过本次试验也使我感到自身编程能力的欠缺,在数据结构课程的学习中还有很多知识没有熟练掌握,动手能力不强,在以后的学习中要不断加强知识的积累,提高自己的动手能力,