数据结构课程设计+哈夫曼编码译码器.docx

上传人:聆听****声音 文档编号:565081 上传时间:2023-04-29 格式:DOCX 页数:16 大小:85.34KB
下载 相关 举报
数据结构课程设计+哈夫曼编码译码器.docx_第1页
第1页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第2页
第2页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第3页
第3页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第4页
第4页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第5页
第5页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第6页
第6页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第7页
第7页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第8页
第8页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第9页
第9页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第10页
第10页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第11页
第11页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第12页
第12页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第13页
第13页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第14页
第14页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第15页
第15页 / 共16页
数据结构课程设计+哈夫曼编码译码器.docx_第16页
第16页 / 共16页
亲,该文档总共16页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

数据结构课程设计+哈夫曼编码译码器.docx

《数据结构课程设计+哈夫曼编码译码器.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计+哈夫曼编码译码器.docx(16页珍藏版)》请在冰点文库上搜索。

数据结构课程设计+哈夫曼编码译码器.docx

哈夫曼编码译码器

哈夫曼编码译码器

a)需求分析:

一个完整的系统应具有以下功能:

(l)I:

初始化。

从终端读入字符集大小n,及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmtree中。

(2)C:

编码。

利用已建好的哈夫曼树(如不在内存,则从文件hfmtree中读入),对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中。

(3)D:

编码。

利用已建好的哈夫曼树将文件codefile中的代码进行译码,结果存入文件textfile中。

(4)P:

印代码文件。

将文件codefile以紧凑格式显示在终端上,每行

50个代码。

同时将此字符形式的编码文件写入文件codeprint中。

(5)T:

印哈夫曼树。

将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件treeprint中

字符

A

B

C

D

E

F

G

H

I

J

K

L

M

频度

186

64

23

22

32

103

21

15

47

57

1

5

32

20

字符

N

O

P

Q

R

S

T

U

V

W

X

Y

Z

频度

20

56

19

2

50

51

55

30

10

11

2

21

2

可以根据题目要求把程序划成5个模块,设计成菜单方式,每次执行一个模块后返回菜单。

除了初始化(I)过程外,在每次执行时都经过一次读取磁盘文件数据。

这是为了如果在程序执行后一直没有进行初始化(I)过程,为了能使后面的操作顺利进行,可以通过读取旧的数据来进行工作。

比如:

如果程序的工作需要的字符集和权值数据是固定的,只要在安装程序时进行一次初始(I)化操作就可以了。

在再次运行程序时,不管进行那项操作都可以把需要的数据读入到内存。

b)概要设计

本程序主要用到了三个算法。

(1)哈夫曼编码

在初始化(I)的过程中间,要用输入的字符和权值建立哈夫曼树并求得哈夫曼编码。

先将输入的字符和权值存放到一个结构体数组中,建立哈夫曼树,将计算所得的哈夫曼编码存储到另一个结构体数组中。

(2)串的匹配

在编码(D)的过程中间,要对已经编码过的代码译码,可利用循环,将代码中的与哈夫曼编码的长度相同的串与这个哈夫曼编码比较,如果相等就回显并存入文件。

(3)二叉树的遍历

在印哈夫曼树(T)的中,因为哈夫曼树也是二叉树,所以就要利用二叉树的先序遍历将哈夫曼树输出

c)详细设计

构造树的方法如下:

初始化:

每个字符就是一个结点,字符的频度就是结点的权;

1、将结点按频度从小到大排序;

2、选取频度最小的 两个结点,以它们为儿子,构造出一个新的结点;新结点的权值就是 它两个儿子的权值之和;

构造之后,从原来的结点序列里删除刚才选出的那两个结点,但同时将新生成的结点加进去;

3、如果结点序列里只剩下一个结点,表示构造完毕,退出。

否则回到第一步。

编码:

上面已经生成了树,接着就该对该树进行编码了。

可以假定,对某个结点而言,其左孩子在当前阶段的编码为 0,右孩子的编码为 1。

这样就可以通过“树的遍历”的方式来生成 字符——编码 对照表。

来到这里,基本上艰苦的已经完成了,对某个具体的字符串编码和解码就只是简单的“查表——替换”的工作了。

解码:

解码也是个简单的查表--替换过程。

如果利用该种编码发送字符串,则它的“字符——编码”对应表也必须发送过去,不然对方是不知道怎么解码的。

对给出的一串编码,从左向右,将编码组合起来并查表,“一旦”找到有匹配的 字符,则马上将当前的 编码 替换为 对应的字符。

因为该编码是不会出现 “某一个字符的编码 是 另一个字符编码 的前缀” 这种情况的,也就是不会出现类似于“A 00 B 0010” 这样的情况,所以解码出来的字符串是唯一的,而且就是原来进行编码的那一个。

程序如下

#include#include#includeusingnamespacestd;

structHuffmanNode //定义哈夫曼树各结点

{

intweight; //存放结点的权值,假设只考虑处理权值为整数的情况

intparent; //记录结点父亲位置,-1表示为根结点,否则表示为非根结点

intlchild,rchild; //分别存放该结点的左、右孩子的所在单元的编号

};

classHuffmanTree //建立哈夫曼树类

{

private:

HuffmanNode*Node; //哈夫曼树中结点的存储结构char*Info; //用来保存各字符信息

intLeafNum; //树中的叶子结点总数public:

HuffmanTree(); //构造函数

~HuffmanTree(); //析构函数

voidInitialization(intWeightNum); //初始化函数:

根据WeightNum个权值建立一棵哈夫曼树

voidEncoder(); //编码函数:

利用构造好的哈夫曼树对字符进行编码voidDecoder(); //译码函数:

对二进制串进行译码

voidPrint(); //印文件函数:

把已保存好的编码文件显示在屏幕

voidTreePrinting(); //印哈夫曼树函数:

将已在内存中的哈夫曼树以直观的方式显示在终端上

};

// 程序名:

HuffmanTree.cpp

// 程序功能:

实现哈夫曼树类的源文件(并用其来实现编/译码)

// 构造函数

// 函数功能:

将结点指针初始化为NULLHuffmanTree:

:

HuffmanTree()

{

Node=NULL; //将树结点初始化为空

Info=NULL; //将字符数组初始化为空

LeafNum=0; //将叶子数初始化为0

}

//析构函数

//函数功能:

将所有结点的空间释放

//函数参数:

//参数返回值:

HuffmanTree:

:

~HuffmanTree()

{

delete[]Node; //释放结点空间

delete[]Info; //释放字符存储空间

}

// 初始化函数

// 函数功能:

从终端读入字符集大小n,以及n个字符和n个权值,

// 建立哈夫曼树,并将它存放在文件hfmTree中.

// 函数参数:

intWeightNum表示代码个数

// 参数返回值:

voidHuffmanTree:

:

Initialization(intWeightNum) //初始化

{

inti,j,pos1,pos2,max1,max2;

Node=newHuffmanNode[2*WeightNum-1]; //WeightNum权值对应的哈夫曼树中的结点总数为2*WeightNum-1个

Info=newchar[2*WeightNum-1];for(i=0;i

{

cout<<"请输入第"<

Info[i]=getchar(); //输入一个字符,主要是考虑输入空格而采用这种形式的getchar();

cout<<"请输入该字符的权值或频度";cin>>Node[i].weight; //输入权值Node[i].parent=-1; //为根结点Node[i].lchild=-1; //无左孩子

Node[i].rchild=-1; //无右孩子

}

for(i=WeightNum;i<2*WeightNum-1;i++)//表示需做WeightNum-1次合并

{

pos1=-1;

pos2=-1; //分别用来存放当前最小值和次小值的所在单元编号max1=32767; //32767为整型数的最大值

max2=32767; //分别用来存放当前找到的最小值和次小值for(j=0;j

if(Node[j].weight

{

max2=max1; //原最小值变为次小值max1=Node[j].weight; //存放最小值pos2=pos1; //修改次小值所在单元编号

pos1=j; //修改最小值所在单元编号

}

else

if(Node[j].weight

{

max2=Node[j].weight; //存放次小值

pos2=j; //修改次小值所在的单元编号

}

//for

Node[pos1].parent=i; //修改父亲位置Node[pos2].parent=i;

Node[i].lchild=pos1; //修改儿子位置Node[i].rchild=pos2;

Node[i].parent=-1; //表示新结点应该是根结点Node[i].weight=Node[pos1].weight+Node[pos2].weight;

}//forLeafNum=WeightNum;

charch;

cout<<"是否要替换原来文件(Y/N):

";cin>>ch;

if(ch=='y'||ch=='Y')

{

ofstreamfop; //以二进制方式打开hfmTree.dat文件,并当重新运行时覆盖原文件

fop.open("hfmTree.dat",ios:

:

out|ios:

:

binary|ios:

:

trunc);if(fop.fail()) //文件打开失败cout<<"文件打开失败!

\n";

fop.write((char*)&WeightNum,sizeof(WeightNum)); //写入WeightNumfor(i=0;i

{

fop.write((char*)&Info[i],sizeof(Info[i]));flush(cout);

}

for(i=0;i<2*WeightNum-1;i++) //把个节点内容写入文件

{

fop.write((char*)&Node[i],sizeof(Node[i]));flush(cout);

}

fop.close(); //关闭文件

}

cout<<"哈夫曼树已构造完成。

\n";

}//Initialization

// 编码函数

// 函数功能:

利用已建立好的哈夫曼树(如不在内存,则从文件hfmTree中读入),

// 对文件ToBeTran中的正文进行编码,然后将结果代码存(传输)到文件

CodeFile

voidHuffmanTree:

:

Encoder()

{

if(Node==NULL) //哈夫曼树不在内存,从文件hfmTree中读入

{

ifstreamfip; //以二进制方式打开hfmTree.dat文件

fip.open("hfmTree.dat",ios:

:

binary|ios:

:

in);if(fip.fail()) //文件打开失败

{

cout<<"文件打开失败!

\n";return; //结束本函数

}

fip.read((char*)&LeafNum,sizeof(LeafNum)); //读取叶子数Info=newchar[LeafNum];

Node=newHuffmanNode[2*LeafNum-1];

for(inti=0;i

}

char*Tree; //用于存储需编码内容inti=0,num;

charChoose; //让用户选择读取文件或重新输入需编码内容cout<<"你要从文件中读取内容

(1),还是重新输入

(2):

";cin>>Choose;

if(Choose=='1') //读取文件ToBeTran.txt

{

ifstreamfip1("ToBeTran.txt");if(fip1.fail()) //文件不存在

{

cout<<"文件打开失败!

\n";return; //结束本函数

}

charch;intk=0;

while(fip1.get(ch))

{

k++; //计算CodeFile中代码长度

}

fip1.close();

Tree=newchar[k+1];

ifstreamfip2("ToBeTran.txt");k=0;

while(fip2.get(ch))

{

Tree[k]=ch; //读取文件内容,并存到Tree中

k++;

}

fip2.close();

Tree[k]='\0'; //结束标志cout<<"需编码内容为:

";cout<

}//if(Choose=='1')

else //Choose!

='1',重新输入

{

stringtree; //用于输入需编码内容,由于string类对象可以输入任意长度,

//所以先利用这个对象输入,再转存在Tree中

cin.ignore();

cout<<"请输入需要编码的内容(可输入任意长,结束时请按2下回车):

\n";getline(cin,tree,'\n'); //输入任意长字符串,

//getline以回车('\n')作为结束符,第一次按回车表示字符串结束,第二次按回车才开始输出。

while(tree[i]!

='\0')i++;

num=i; //计算tree长度

i=0;

Tree=newchar[num+1];

while(tree[i]!

='\0') //将tree中的字符转存到Tree中

{

Tree[i]=tree[i];i++;

}

Tree[i]='\0'; //结束标志符

}

ofstreamfop("CodeFile.dat",ios:

:

trunc); //存储编码后的代码,并覆盖原文件i=0;

intk=0;char*code;

code=newchar[LeafNum]; //为所产生编码分配容量为LeafNum的存储空间

//因为不等长编码中最长的编码一定不会超过要求编码

的字符个数

while(Tree[k]!

='\0') //对每一个字符编码

{

intj,start=0;for(i=0;i

if(Info[i]==Tree[k]) //求出该文字所在单元的编号break;

j=i;

while(Node[j].parent!

=-1) //结点j非树根

{

j=Node[j].parent; //非结点j的双亲结点

if(Node[j].lchild==i) //是左子树,则生成代码0code[start++]='0';

else //是右子树,则生成代码1code[start++]='1';\

i=j;

}

code[start]='\0'; //置串结束符

for(i=0;i

{

j=code[i];code[i]=code[start-i-1];code[start-i-1]=j;

}

i=0;

while(code[i]!

='\0') //存储代码

{

fop<

}

k++;

}

fop.close();

cout<<"已编码!

且存到文件CodeFile.dat中!

\n\n";

} //Encode

// 译码函数

// 函数功能:

利用已建好的哈夫曼树,对传输到达的CodeFile中的数据代码进行译码,

// 将译码结果存入文件TextFile中.voidHuffmanTree:

:

Decoder()

{

inti=0,k=0;

intj=LeafNum*2-1-1; //表示从根结点开始往下搜索char*BitStr;

ifstreamfip1("CodeFile.dat"); //利用已建好的哈夫曼树将文件CodeFile中的代码进行译码

if(fip1.fail()) //文件打开失败,还未编码

{

cout<< "请先编码!

\n";return;

}

cout<<"经译码,原内容为:

";charch;

while(fip1.get(ch))

{

k++; //计算CodeFile中代码长度

}

fip1.close();

BitStr=newchar[k+1];ifstreamfip2("CodeFile.dat");k=0;

while(fip2.get(ch))

{

BitStr[k]=ch; //读取文件内容k++;

}

fip2.close();

BitStr[k]='\0'; //结束标志符if(Node==NULL) //还未建哈夫曼树

{

cout<<"请先编码!

\n";return;

}

ofstreamfop("TextFile.dat"); //将字符形式的编码文件写入文件CodePrin中

while(BitStr[i]!

='\0')

{

if(BitStr[i]=='0')

j=Node[j].lchild; //往左走else

j=Node[j].rchild; //往右走

if(Node[j].rchild==-1) //到达叶子结点

{

cout<

j=LeafNum*2-1-1; //表示重新从根结点开始往下搜索fop<

}//if、i++;

}//whilefop.close();

cout<<"\n译码成功且已存到文件TextFile.dat中!

\n\n";

}//Decoder

// 印文件代码函数

// 函数功能:

将文件CodeFile以紧凑格式显示在终端上,

// 每行50个代码。

同时将此字符形式的编码文件写入文件CodePrin中。

// 函数参数:

// 参数返回值:

voidHuffmanTree:

:

Print()

{

charch;inti=1;

ifstreamfip("CodeFile.dat"); //读取文件

ofstreamfop("CodePrin.dat"); //存储文件if(fip.fail())

{

cout<<"没有文件,请先编码!

\n";return;

}

while(fip.get(ch))

{

cout<

fop<

if(i==50) //每行输出50个字符

{

cout<

}

i++;

}

cout<

fip.close(); //关闭CodeFile.dat文件

fop.close(); //关闭CodePrin.dat文件

}

// 显示哈夫曼树函数

// 函数功能:

将已在内存中的哈夫曼树以直观的方式(树或凹入表的形式)显示在终端上,

// 同时将此字符形式的哈夫曼树写入文件TreePrint中。

voidHuffmanTree:

:

TreePrinting()

{

if(Node==NULL) //未建立哈夫曼树

{

cout<<"请先建立哈夫曼树!

\n";return;

}

ofstreamfop("TreePrint.dat");

cout<<"结点位置(权值) "<<"编码 "<<"左孩子 "<<"编码"<<"右孩子('^'表示叶子)\n";

fop<<"结点位置(权值) "<<"编码 "<<"左孩子 "<<"编码"<<"右孩子('^'表示叶子)\n";inti;

for(i=(2*LeafNum-2);i>LeafNum-1;i--) //输出哈夫曼树

{

cout<

<

<

<

<

}

for(;i>=0;i--)

{

cout<

"<

fop<

"<

}

}

// 程序功能:

主函数源文件

// 主函数

intmain()

{

cout<<" 欢迎使用哈夫曼码的编/译码系统!

\n";

cout<<"在此系统中可以进行以下操作:

\n";cout<<"

(1)初始化(I);\n";

cout<<"

(2)编码(E);\n";

cout<<"(3)译码(D);\n";

cout<<"(4)代码文件(P);\n";

cout<<"(5)印哈夫曼树(T)\n";

cout<<"(6)退出(Q)\n\n";

HuffmanTreehuftree; //定义哈夫曼树对象intweight;

charChoose;

while

(1)

{

cout<<"请从清单中选择一个操作(不区分大小写):

";cin>>Choose;

switch(Choose)

{

case'I':

case'i':

cout<<"请输入编码长度:

";cin>>weight;

huftree.Initialization(weight); //初始化哈夫曼树break;

case'E':

case'e':

huftree.Encoder();break;

case'D':

case'd':

huftree.Decoder();break;

case'

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

当前位置:首页 > IT计算机 > 电脑基础知识

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

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