数据结构实习哈夫曼编码文档格式.docx

上传人:b****5 文档编号:21840184 上传时间:2023-02-01 格式:DOCX 页数:27 大小:2.13MB
下载 相关 举报
数据结构实习哈夫曼编码文档格式.docx_第1页
第1页 / 共27页
数据结构实习哈夫曼编码文档格式.docx_第2页
第2页 / 共27页
数据结构实习哈夫曼编码文档格式.docx_第3页
第3页 / 共27页
数据结构实习哈夫曼编码文档格式.docx_第4页
第4页 / 共27页
数据结构实习哈夫曼编码文档格式.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

数据结构实习哈夫曼编码文档格式.docx

《数据结构实习哈夫曼编码文档格式.docx》由会员分享,可在线阅读,更多相关《数据结构实习哈夫曼编码文档格式.docx(27页珍藏版)》请在冰豆网上搜索。

数据结构实习哈夫曼编码文档格式.docx

4本次作业心得................................................................24

1.题目

设二叉树以二叉链表的形式保存,T为指向根结点的指针。

试完成以下功能:

1、建立二叉树:

从键盘输入各结点的值,可参照二叉树的顺序存储方式。

例如输入“a,b,c,,d”表示结点a是根,b和c是a的两个孩子,b仅有右孩子d。

2、统计T中叶结点的个数。

3、统计T中度为2的结点的个数。

4、求树T的高度。

5、判断T中是否有度为1的结点(即按照国外教材的定义,是否为满树)。

在此基础上,实现Huffman编码及译码,达到以下两个目标:

1、掌握二叉树的二叉链表存贮结构。

2、掌握Huffman算法。

2.要求

使用文件保存初始的文本数据及最终的结果。

●文件名为inputfile1.txt的文件保存的是一段英文短文;

●文件名为inputfile2.txt的文件保存01形式的编码段;

●文件名为outputfile1.txt的文件保存各字符的出现次数和对应的编码;

●文件名为outputfile2.txt的文件保存对应于inputfile2.txt的译码结果。

统计inputfile1.txt中各字符的出现频率,并据此构造Huffman树,编制Huffman编码;

根据已经得到的编码,对01形式的编码段进行译码。

具体的要求:

1.将给定字符文件编码:

生成编码,输出每个字符出现的次数和编码;

2.将给定编码文件译码:

生成字符,输出编码及其对应字符。

3.程序实现

3.1程序运行及编译环境

程序是用VisualStudio2010即VS10.0编译的。

可以在windows系列的操作系统上运行。

3.2程序描述

该程序主要用于实现二叉树的二叉链表保存并实现Huffman编码以及译码。

其流程如下:

A).程序读取输入文件,并生成相应的数据结构

a.用数组来声明并存储树,完成初始化的工作

b.读入文本内容,并统计每个字符的频率

c.根据每个字母的权重,对森林里每棵树的相应参数重新赋值

d.将森林合并为树

B).输出相关信息

a.在文本里依次输出字符出现的次数,频率以及编码

B.在控制台输出统计的字母频率(其实这个不是必要的)

C).编码以及译码

a.在统计好的字符出现次数的基础上编码

b.编码前的两个文件比较

c.对编码的文件译码

d.译码后的两个文件比较

D).完成

3.3实现功能

 

3.3.1子功能模块1

读入文本,统计频率。

//开始读入文件

while(fin1.get(c)){//读入文件

let[c].weight++;

//统计每个字符的权重

CharCount++;

if(c<

0||c>

127){

cout<

<

"

打开文件在一以下位置出现错误:

;

fin1.get(c);

fin1.get(c);

for(inti=0;

i<

TIPS;

i++){fin1.get(c);

cout<

c;

}

的前面"

temp_char<

的后面"

endl;

可能原因有:

①文本编码为UTF-8,Unicode,Unicodebigendian等非ANSI编码;

"

②或者文本含有中文字符或者全角字符"

endl<

请核查!

return1;

}

if((c>

='

a'

&

c<

z'

)||(c>

A'

Z'

))

LetterCount++;

for(inti=0;

i++)

temp_char[i]=temp_char[i+1];

temp_char[TIPS]=c;

fin1.close();

//读入完成

3.3.1.1输入项

输入项有:

fin1:

文件输入流

3.3.1.2输出项

let[c].weight;

//每个字符的出现次数

CharCount;

//所有字符出现次数总和

//文件流读入的每个字符

temp_char;

//保存当前读到字符的前几个字符,以便于在含有Unicode,UTF-8等编码的字符时,及时找出出错字符位置

3.3.1.3数据结构的定义

3.3.1.3.1全局数据结构

全局数据结构有:

//用一个结构来存储字符,统计的权重,以及该字母在哈夫曼树里面的偏移,这样可以快速定位到该字母的结点

typedefstructLet{

charletter;

//字符

intoffset;

//字母在哈夫曼树里面的偏移

intweight;

//字符的权重

doublefreq;

//freq表示在所有字符中的频率

doubleletterFreq;

//表示字母在所有字母中的频率

};

typedefstructHuffNode{

//letter表示结点的字符

//weight表示权重

HuffNode*left;

//左孩子

HuffNode*right;

//右孩子

boolmerge;

//merge表示能否合并,如果两棵树合并后,就不可以合并了

boolcode[MAXCODELENGTH];

//0表示左边,1表示右边

intValidCode;

//ValidCode表示前多少位的编码是有效的

classHuffTree{

HuffNode*root;

//树的根节点

public:

HuffTree(charletter,intweight);

HuffTree();

HuffNode*getRoot();

voidsetRoot(HuffNode*Root);

HuffTree*HuffTreeMerge(HuffTree*Tree1,HuffTree*Tree2);

HuffTree*CreateHuffTree(HuffTreeHN[],intCOUNT);

voidMidOrder(HuffNode*node);

voidPreOrder(HuffNode*node);

voidHuffCode(HuffNode*root);

}

3.3.1.32局部数据结构

3.3.1.4算法及程序说明

本程序中重要的算法思想是:

给出了一种编码的时候不同的遍历方法。

把树中所有的结点都对应到一个整数(不同的结点,对应不同的整数),然后按照整数从小到大的顺序,依次找到其结点并且访问,编码。

这个对应的关系式这样的:

一个整数,例如10,那么它的二进制表示为1010B,于是,把最高位1去掉,剩下的是010B,然后这个010B对应的是从根节点的左子树的右子树的左子树对应的结点,(就是0表示左子树,1表示右子树)并且根据这个二进制数,可以很快的找到这个结点,而且每次都是从根节点开始,没有系统栈的开销。

不同的结点,对应不同的整数是显然的,因为从根节点到达不同的结点的次序是不同的,因此这些次序对应的二进制代码也不一样,在其前面加一项1以后,还是不一样的。

为此,需要用到一下两个函数作为基础

A).本程序最重要的是这几个函数:

1.voidConvert2Bin(intnum,boola[],int&

idx);

把一个十进制数转化为二进制数

num:

要转化的十进制数;

boola[]:

为节省空间,采用bool型来存储二进制的每一位;

idx表示

代码如下:

voidConvert2Bin(intnum,boola[],int&

idx){//把一个整数num转化为二进制的形式,存放在数组a[]里面,idx存放输出到数组的哪一项

inti=0;

booltemp;

while(num>

1){//除2取余,得到逆序的二进制序列

a[i++]=num%2;

num/=2;

a[i]=num;

idx=i;

for(intj=0;

j<

(idx+1)/2;

j++)//把逆序的变为正序的

{

temp=a[j];

a[j]=a[idx-j];

a[idx-j]=temp;

2.voidCodingOrder(intnum,boola[],int&

由num决定编码二叉树每个结点的顺序num表示数的序号;

a[]这个数组存遍历的序列;

idx表示这个数组的前多少项有效

voidCodingOrder(intnum,boola[],int&

idx){//

Convert2Bin(num,a,idx);

for(inti=0;

idx;

a[i]=a[i+1];

idx--;

B).其他用到的算法

森林合并为树的算法:

每两棵树合并成一棵新树,一直合并直到只剩下一棵树,森林就变成树了。

Huffman编码算法:

从1到MAXSIZE*MAXSIZE的所有整数都有一个二进制形式,然后取

除第一位以外的所有二进制位作为遍历的顺序,如果遇到根节点

就把那个二进制的顺序作为该节点的编码,遇到合并以后的结点

继续往下走,如果编码的位数更多,只要把最大遍历整数调大即可

算法代码如下:

voidHuffCode(HuffNode*root){

boolorder[MAXCODELENGTH];

//存放编码的数组

intcount=0;

//count表示该数组的前多少位表示编码

HuffNode*temp=newHuffNode;

//用于遍历

for(inti=1;

MAXSIZE*MAXSIZE;

i++){

temp=root;

//每个数都是从头开始找

CodingOrder(i,order,count);

//得到这个数对应的遍历顺序

for(intj=0;

=count;

j++){

switch(order[j]){

casetrue:

//true时,右走

temp=temp->

right;

break;

casefalse:

//false时,左走

left;

default:

cout<

Error!

//调试的时候,快速定位出错点

}

if(temp->

letter){//到达一个有字母的结点,这时候就可以得到编码

temp->

ValidCode=j;

for(intk=0;

k<

=temp->

ValidCode;

k++)

temp->

code[k]=order[k];

break;

}

}

C).体会

算法并不是什么代码,也不是什么规则,方法只要能用有限的步骤实现相应的功能,这就是一个算法。

由于开始时,我死按着常规的顺序去遍历树,想去得到每个节点的编码,发现递归遍历代码容易出错,非递归遍历代码太复杂,以至于我写的每一次遍历都得不到正确结果,所以后来受第一个习题的影响,用了第一个习题里面的思想。

3.3.2子功能模块2

用于实现两棵树合并成一棵新树

Tree1,Tree2,分别表示两棵树指针,返回两棵树合并后形成的一颗新树

HuffTree*HuffTreeMerge(HuffTree*Tree1,HuffTree*Tree2){

HuffTree*Tree=newHuffTree;

if(Tree1->

root->

merge==false||Tree2->

merge==false)//两棵树均不可合并时,给出错误提示

{

Cannotmergeintooneroot!

returnTree1;

Tree->

weight=Tree1->

weight+Tree2->

weight;

//新树的权重是原来两棵树的权重之和

merge=true;

//新树标记为可以合并

Tree1->

merge=false;

//原来那两棵树均不可合并

Tree2->

left=Tree1->

root;

//Tree1为左子树

right=Tree2->

//Tree为右子树

returnTree;

3.3.3子功能模块3

HuffTree*CreateHuffTree(HuffTreeHN[],intCOUNT);

HN[]表示分立的树;

COUNT表示树的棵树,返回形成的那棵哈夫曼树的根;

实现方法:

先取出可以合并的两棵树,然后往后找,发现权重比那两棵小的就取权重小树

直到最后,然后,两棵树合二为一,合成的新树依次放在原来数组的后面,直到原来

所有的树都合并完毕。

HuffTree*CreateHuffTree(HuffTreeHN[],intCOUNT){//count表示可以用来合并成为哈夫曼树的结点个数

HuffTree*minTree1;

HuffTree*minTree2;

HuffTree*temp;

inti=0;

intcount=COUNT;

intappend=COUNT;

while(count>

1)//还没有合并完毕

for(i=0;

2*COUNT-1;

i++)//找第一课可以合并的树

if((HN+i)->

merge==true&

(HN+i)->

weight!

=0)

{

minTree1=HN+i;

i++;

break;

for(;

i++)//找第二课可以合并的树

minTree2=HN+i;

if(minTree1->

weight>

minTree2->

weight)//使得minTree1总是小于minTree2;

temp=minTree1;

minTree1=minTree2;

minTree2=temp;

for(;

i++)//后面要是有可以合并的权重比之更小的树就取那棵树用于合并

if((HN+i)->

{

weight<

minTree1->

weight)

minTree2=minTree1;

minTree1=HN+i;

else

minTree2->

root=HuffTreeMerge(minTree1,minTree2)->

//两棵树合并成为一棵树

HN[append++].root=Tree->

//新合成的树置于原来数组后面

count--;

3.3.4子功能模块4

函数原型:

voidMidOrder(HuffNode*node);

函数参数:

树的根节点

实现方法:

递归,中序遍历

代码:

voidMidOrder(HuffNode*node){//中序递归遍历,并且输出编码

if(node)

MidOrder(node->

left);

if(node->

letter){

cout<

char(node->

letter)<

weight:

node->

weight<

Code:

=node->

code[i];

right);

3.4运行结果

A).程序基本功能的实现:

图1控制台输出每个字符的权重以及编码

图2控制台输出每个字符的权重以及编码

图3编码的文件inputfile1.txt以及解码后的文件outputfile2.txt文件大小均为19KB

图4编码的文件inputfile1.txt

图5解码后的文件outputfile2.txt

由于这样的题目比较有意思,而且输入的文本是大学两年来写过的所有英语作文,这些数据是在十几篇英语作文里面的含空格,回车等一切标点的19053个字符,1019个互不相同的单词的数据基础上得出的。

所以,除了实现以上功能外,我还特地统计了自己写的作文的一些统计特性,比如每个英文字符在所有英文字符的频率,发现还是e的频率最高,当然了,程序中e和E算作不同的字母。

B).附加功能:

图6统计所有字符的频率,如果是英文字符,还算其在所有英文字符这个群体里的频率。

这在一定程度上反映了语言的特性,利用一个更加大的更具有普遍性的语料库,可以统计所有字母的频率,有一种古老破解密码的方法就是基于这种大语料库下字符频率的,虽然密文不是严格按照这个频率分布的,但是频率最大的几个字母是不变的,这就为破解密码提供了可能,当然了,现代密码学是建立在数论基础上的公开密钥的方法,密文不再具有统计特性。

扯得有点远,其

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 其它模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1