哈夫曼树实验报告.docx

上传人:b****7 文档编号:15580618 上传时间:2023-07-05 格式:DOCX 页数:20 大小:71.16KB
下载 相关 举报
哈夫曼树实验报告.docx_第1页
第1页 / 共20页
哈夫曼树实验报告.docx_第2页
第2页 / 共20页
哈夫曼树实验报告.docx_第3页
第3页 / 共20页
哈夫曼树实验报告.docx_第4页
第4页 / 共20页
哈夫曼树实验报告.docx_第5页
第5页 / 共20页
哈夫曼树实验报告.docx_第6页
第6页 / 共20页
哈夫曼树实验报告.docx_第7页
第7页 / 共20页
哈夫曼树实验报告.docx_第8页
第8页 / 共20页
哈夫曼树实验报告.docx_第9页
第9页 / 共20页
哈夫曼树实验报告.docx_第10页
第10页 / 共20页
哈夫曼树实验报告.docx_第11页
第11页 / 共20页
哈夫曼树实验报告.docx_第12页
第12页 / 共20页
哈夫曼树实验报告.docx_第13页
第13页 / 共20页
哈夫曼树实验报告.docx_第14页
第14页 / 共20页
哈夫曼树实验报告.docx_第15页
第15页 / 共20页
哈夫曼树实验报告.docx_第16页
第16页 / 共20页
哈夫曼树实验报告.docx_第17页
第17页 / 共20页
哈夫曼树实验报告.docx_第18页
第18页 / 共20页
哈夫曼树实验报告.docx_第19页
第19页 / 共20页
哈夫曼树实验报告.docx_第20页
第20页 / 共20页
亲,该文档总共20页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

哈夫曼树实验报告.docx

《哈夫曼树实验报告.docx》由会员分享,可在线阅读,更多相关《哈夫曼树实验报告.docx(20页珍藏版)》请在冰点文库上搜索。

哈夫曼树实验报告.docx

哈夫曼树实验报告

数据结构实验报告

实验名称:

实验三哈夫曼树

学生姓名:

班级:

班内序号:

学号:

日期:

 

程序分析:

2.1存储结构:

二叉树

2.2程序流程:

template

classBiTree

{

public:

BiTree();//构造函数,其前序序列由键盘输入

~BiTree(void);//析构函数

BiNode*Getroot();//获得指向根结点的指针

protected:

BiNode*root;//指向根结点的头指针

};

//声明类BiTree及定义结构BiNode

Data:

二叉树是由一个根结点和两棵互不相交的左右子树构成

二叉树中的结点具有相同数据类型及层次关系

示意图:

root

lchildparentrchild

 

哈夫曼树类的数据域,继承节点类型为int的二叉树

classHuffmanTree:

publicBiTree

data:

HCode*HCodeTable;//编码表

inttSize;//编码表中的总字符数

二叉树的节点结构

template

structBiNode//二叉树的结点结构

{

Tdata;//记录数据

Tlchild;//左孩子

Trchild;//右孩子

Tparent;//双亲

};

示意图:

 

编码表的节点结构

structHCode

{

chardata;//编码表中的字符

charcode[100];//该字符对应的编码

};

示意图:

 

待编码字符串由键盘输入,输入时用链表存储,链表节点为

structNode

{

charcharacter;//输入的字符

unsignedintcount;//该字符的权值

boolused;//建立树的时候该字符是否使用过

Node*next;//保存下一个节点的地址

};

示意图:

 

2.3关键算法分析:

1.初始化函数〔voidHuffmanTree:

:

Init(stringInput)〕

算法伪代码:

1.初始化链表的头结点

2.获得输入字符串的第一个字符,并将其插入到链表尾部,n=1(n记录的是链表中字符的个数)

3.从字符串第2个字符开始,逐个取出字符串中的字符

3.1将当前取出的字符与链表中已经存在的字符逐个比拟,如果当前取出的字符与链表中已经存在的某个字符相同,那么链表中该字符的权值加1。

3.2如果当前取出的字符与链表中已经存在的字符都不相同,那么将其参加到链表尾部,同时n++

4.tSize=n(tSize记录链表中字符总数,即哈夫曼树中叶子节点总数)

5.创立哈夫曼树

6.销毁链表

源代码:

voidHuffmanTree:

:

Init(stringInput)

{

Node*front=newNode;//初始化链表的头结点

if(!

front)

throwexception("堆空间用尽");

front->next=NULL;

front->character=NULL;

front->count=0;

Node*pfront=front;

charch=Input[0];//获得第一个字符

Node*New1=newNode;

if(!

New1)

throwexception("堆空间用尽");

New1->character=ch;//将第一个字符插入链表

New1->count=1;

New1->next=pfront->next;

pfront->next=New1;

boolreplace=0;//判断在已经写入链表的字符中是否有与当前读出的字符相同的字符

intn=1;//统计链表中字符个数

for(inti=1;i

{

ch=Input[i];//获得第i个字符

do

{

pfront=pfront->next;

if((int)pfront->character==(int)ch)//如果在链表中有与当前字符相同的字符,该字符权值加1

{

pfront->count++;

replace=1;

break;

}

}while(pfront->next);

if(!

replace)//如果在链表中没找到与当前字符相同的字符,那么将该字符作为新成

员插入链表

{

Node*New=newNode;

if(!

New)

throwexception("堆空间用尽");

New->character=ch;

New->count=1;

New->next=pfront->next;

pfront->next=New;

n++;

}

pfront=front;//重置pfront和replace变量为默认值

replace=0;

}

tSize=n;//tSize记录的是编码表中字符个数

CreateHTree(front,n);//创立哈夫曼树

pfront=front;

while(pfront)//销毁整个链表

{

front=pfront;

pfront=pfront->next;

deletefront;

}

时间复杂度:

假设输入的字符串长度为n,那么时间复杂度为O(n)

2.创立哈夫曼树〔voidHuffmanTree:

:

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.根据哈夫曼树创立编码表

源代码:

voidHuffmanTree:

:

CreateHTree(Node*p,intn)

{

root=newBiNode[2*n-1];//初始化哈夫曼树

Node*front=p->next;

if(n==0)

throwexception("没有输入字符");

for(inti=0;i

{

root[i].data=front->count;

root[i].lchild=-1;

root[i].rchild=-1;

root[i].parent=-1;

front=front->next;

}

front=p;

intNew1,New2;

for(i=n;i<2*n-1;i++)

{

SelectMin(New1,New2,0,i);//从0~i中选出两个权值最小的结点

root[New1].parent=root[New2].parent=i;//用两个权值最小的结点生成新结点,新节点为其双亲

root[i].data=root[New1].data+root[New2].data;//新结点的权值为其孩子的权值的和

root[i].lchild=New1;

root[i].rchild=New2;

root[i].parent=-1;

}

CreateCodeTable(p);//创立编码表

}

时间复杂度:

在选取两个权值最小的结点的函数中要遍历链表,时间复杂度为O(n),故该函数的时间复杂度为O〔n^2〕

3.创立编码表〔voidHuffmanTree:

:

CreateCodeTable(Node*p)〕

算法伪代码:

1.初始化编码表

2.初始化一个指针,从链表的头结点开始,遍历整个链表

2.1将链表中指针当前所指的结点包含的字符写入编码表中

2.2得到该结点对应的哈夫曼树的叶子结点及其双亲

2.3如果哈夫曼树只有一个叶子结点,将其字符对应编码设置为0

2.4如果不止一个叶子结点,从当前叶子结点开始判断

2.4.1如果当前叶子结点是其双亲的左孩子,那么其对应的编码为0,否那么为1

2.4.2child指针指向叶子结点的双亲,parent指针指向child指针的双亲,重复的操作

2.5将已完成的编码倒序

2.6取得链表中的下一个字符

3.输出编码表

源代码:

voidHuffmanTree:

:

CreateCodeTable(Node*p)

{

HCodeTable=newHCode[tSize];//初始化编码表

Node*front=p->next;

for(inti=0;i

{

HCodeTable[i].data=front->character;//将第i个字符写入编码表

intchild=i;//得到第i个字符对应的叶子节点

intparent=root[i].parent;//得到第i个字符对应的叶子节点的双亲

intk=0;

if(tSize==1)//如果文本中只有一种字符,它的编码为0

{

HCodeTable[i].code[k]='0';

k++;

}

while(parent!

=-1)//从第i个字符对应的叶子节点开始,寻找它到根结点的路径

{

if(child==root[parent].lchild)//如果当前结点为双亲的左孩子,那么编码为0,否那么编码为1

HCodeTable[i].code[k]='0';

else

HCodeTable[i].code[k]='1';

k++;

child=parent;

parent=root[child].parent;

}

HCodeTable[i].code[k]='\0';

Reverse(HCodeTable[i].code);//将编码逆置

front=front->next;//得到下一个字符

}

cout<<"编码表为:

"<

for(i=0;i

{

cout<

}

}

时间复杂度:

需要遍历哈夫曼树获取编码,时间复杂度为O〔n^2〕

4.选择两个最小权值的函数

算法伪代码:

1.从下标为begin的结点开始,寻找第一个没用过的结点

2.遍历哈夫曼树中从下标为begin到下标为end的结点序列,寻找没用过的同时权值又是最小的结点。

3.暂时改变找到的权值最小结点的双亲域,防止第2次找到相同的结点。

4.将权值最小结点的下标记录下来。

5.重复步骤1~4,找到第2个权值最小的结点

源代码:

voidHuffmanTree:

:

SelectMin(int&New1,int&New2,intbegin,intend)

{

intmin;

for(intj=0;j<2;j++)//要选择两个权值最小的结点

{

intsign=begin;

for(inti=begin;i

{

if(root[i].parent==-1)//没用过的结点其双亲应为空

{

min=root[i].data;

sign=i;

break;

}

}

for(i=begin;i

{

if(root[i].parent==-1)

{

if(min>root[i].data)

{

min=root[i].data;

sign=i;

}

}

}

root[sign].parent=0;//暂时改变所找最小结点的双亲域,防止第2次找到的是同一个结点

if(!

j)

New1=sign;

else

New2=sign;

}

}

时间复杂度:

两次遍历链表,时间复杂度为O〔n〕

5.将字符串倒序的函数〔voidHuffmanTree:

:

Reverse(char*pch)〕

算法伪代码:

1.得到字符串的长度

2.初始化两个记录下标的变量,一个为字符串开头字符所在的下标i,另一个为字符串结尾字符所在的下标j

3.将下标为i和j的字符交换

4.i++,j--

时间复杂度:

时间复杂度为O〔n〕

6.编码函数〔voidHuffmanTree:

:

Encode(string&s,string&d)〕

算法伪代码:

1.从s开头的字符开始,逐一对s中的字符进行编码

2.在编码表中查找与当前字符对应的字符

3.如果找到了与当前字符对应的编码表中的字符,将其编码追加到解码串的末尾。

4.重复以上步骤,直到所有待编码串中的字符都编码完毕

5.输出编码后的字符串

源代码:

voidHuffmanTree:

:

Encode(string&s,string&d)

{

for(intj=0;j

{

for(inti=0;i

{

if(s[j]==HCodeTable[i].data)

{

d.append(HCodeTable[i].code);//编码

break;

}

}

}

cout<

}

时间复杂度:

设待编码字符串长度为n,编码表中字符个数为m,那么复杂度为O〔n*m〕

7.解码函数

算法伪代码:

1.得到指向哈夫曼树的根结点的指针和指向待解码串中的第1个字符的指针

2.逐个读取待解码串中的字符,假设为0,那么指向哈夫曼树当前结点的指针指向当前结点的左孩子,假设为1,那么指向当前结点的右孩子

3.指向待解码串的指针指向解码串中的下一个字符,直到指向哈夫曼树结点的指针的孩子结点为空

4.如果哈夫曼树只有一个叶子结点,直接将待解码串中的编码转换为对应的字符

5.如果指向哈夫曼树结点的指针的孩子结点已经为空,那么将叶子结点下标对应的字符追加到解码串中。

6.输出解码串

源代码:

voidHuffmanTree:

:

Decode(string&s,string&d)

{

for(inti=0;i

{

intparent=2*tSize-1-1;//得到哈夫曼树的根结点

while(root[parent].lchild!

=-1)//如果结点不为叶子结点

{

if(s[i]=='0')//编码为0那么寻找其左孩子

parent=root[parent].lchild;

else//编码为1那么寻找右孩子

parent=root[parent].rchild;

i++;

}

if(tSize==1)//如果编码表只有一个字符,那么根结点即为叶子结点

i++;

d.append(1,HCodeTable[parent].data);//将叶子节点对应的字符追加到解码串中

}

cout<

}

时间复杂度:

设待解码串长度为n,那么复杂度为O(n)

8.计算哈夫曼编码的压缩比〔voidHuffmanTree:

:

Calculate(strings1,strings2)〕

算法伪代码:

1.获得编码前字符串的长度,即其占用的字节数

2.获得编码后的字符串的长度,将其除以8然后向上取整,得到其占用的字节数

3.压缩比将两个相除

源代码:

voidHuffmanTree:

:

Calculate(strings1,strings2)

{

intcal1=s1.length();

intcal2=s2.length();

cal2=ceill((float)cal2/8);//将编码串的比特数转化为字节数

cout<<"编码前的字符串长度:

"<

cout<<"编码后的字符串长度:

"<

cout<<"压缩比为:

"<<((double)cal2/(double)cal1)*100<<"%"<

}

时间复杂度:

O

(1)

9.打印哈夫曼树〔voidHuffmanTree:

:

PrintTree(intTreeNode,intlayer)〕

算法伪代码:

1.如果待打印结点为空,那么返回

2.递归调用函数打印当前结点的右子树

3.根据当前结点所在的层次确定其前面要输出多少空格,先输出空格,在打印当前结点的权值

4.递归调用函数打印当前结点的左子树

源代码:

voidHuffmanTree:

:

PrintTree(intTreeNode,intlayer)

{

if(TreeNode==-1)//如果待打印结点为空,那么返回

return;

else

{

PrintTree(root[TreeNode].rchild,layer+1);//先打印该结点的右子树,layer记录的是该结点所在的层次

for(inti=0;i

cout<<'';

cout<

PrintTree(root[TreeNode].lchild,layer+1);//打印该结点的左子树

}

}

时间复杂度:

中序遍历哈夫曼树,复杂度为O(n)

10.菜单函数〔voidHuffmanTree:

:

Menu()〕

算法伪代码:

1.逐一读取键盘缓存区中的字符,并将它们逐一追加到记录输入字符串的string变量中,直到读到回车输入符为止

2.删除string变量末尾的回车输入符

3.利用string变量创立哈夫曼树,初始化编码表。

4.直观打印哈夫曼树

5.对输入的字符串进行编码

6.对编码后的字符串进行解码

7.计算编码前后的压缩比并输出

源代码:

voidHuffmanTree:

:

Menu()

{

cout<<"请输入你要编码的文本,按回车键确定输入"<

stringInput;

charletter;

do//将字符逐个读入Input变量中

{

letter=cin.get();

Input.append(1,letter);

}while(letter!

='\n');

Input.erase(Input.length()-1,1);//去掉Input末尾的回车符

Init(Input);//根据输入的字符串创立哈夫曼树及其编码表

cout<<"直观打印哈夫曼树"<

PrintTree(2*tSize-1-1,1);//打印哈夫曼树

cout<<'\n'<<'\n';

stringd1,d2;

cout<<"编码后的字符串为"<

Encode(Input,d1);//编码并打印编码串

cout<<"解码后的字符串为"<

Decode(d1,d2);//解码并打印解码串

cout<<"ASCII码编码与HUFFMAN编码的比拟"<

Calculate(Input,d1);//计算编码前后的压缩比

}

2.4其他

1.由于题目要求能输入任意长的字符串,所以本程序采用了string变量来记录输入的字符串,并采用string类的类成员函数来完成各项任务

2.打印哈夫曼树时采用了递归函数,且采用了凹凸表的形式打印哈夫曼树。

3.为了输入空格,输入时采取逐个字符输入的方式

三.程序运行结果分析:

主函数流程图:

 

运行结果

各函数运行正常,没有bug

四.总结:

在实现整个算法设计中运用了二叉树结构及类创新,同时又复习了上学期C++的相应内容。

总结与流程分析过程虽然很辛苦但屡败屡战,获益匪浅,收获了很多。

也认识到自己的缺乏,希望自己继续努力。

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

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2