《数据结构课程设计》题目3 哈夫曼算法的应用

问题描述

在一个加密应用中,要处理的信息来自下面的字符集,各个字符的相关使用频度如下:

 

字符

A

B

C

D

E

F

G

H

I

J

K

L

M

频度

180

64

13

23

32103

22

15

47

57

1

5

31

20

字符

N

O

P

Q

R

S

T

U

V

W

X

Y

Z

频度

55

63

15

1

48

56

80

25

7

18

2

16

1

 

现请编写程序你实现如下功能:

(1)运行时,由用户输入来初始化字符集大小和相应用字符。

(2)输入一个要加密的字符串,将其加密。

(3)输出解密字符串。

 

问题分析

根据上述问题描述,可以看出编写的程序是通过利用二叉树结构实现哈夫曼编码和译码,并且程序需具有以下要求:

(1)   初始化:能够让用户输入字符和相应字符的频度来初始化,并建立哈夫曼树。

(2)   建立编码表:利用已经建好的哈夫曼树进行编码,并将每个字符的编码进行输出,打印哈夫曼编码表。

(3)   译码:利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。

运用哈夫曼算法的相关知识解决问题。以用户输入的字符作为需要编码的字符集合,而每个字符对应的频度则作为该字符的权值,构造一棵哈弗曼编码树,规定哈弗曼编码树的左分支代表0,右分支代表1,则从根结点到每个叶子结点所经过的路径组成的0和1的序列便为需要加密字符的编码。

本次课程设计内容涉及到哈夫曼树,哈夫曼编码,哈夫曼算法,二叉树的结构,二叉树和树的应用等等方面知识。

 

逻辑结构和存储结构设计

1.      逻辑结构设计

逻辑结构:数据的逻辑结构是树状结构。

2.      存储结构设计

(1)    采用静态的三叉链表存放。

算法思想:

申请存储哈夫曼编码串的头指针数组,申请一个字符型指针,用来存放临时的编码串;

从叶子节点开始向上倒退,若其为它双亲节点的左孩子则编码标0,否则标1;直到根节点为止,最后把临时存储编码复制到对应的指针数组所指向的内存中;

重复上述步骤,直到所有的叶子节点都被编码完;

(2)    设计一个结构体Element保存哈夫曼树中各结点的信息,包括:

Weight保存结点的权值;

Lchild保存结点的左孩子在数组中的下标;

Rchild保存结点的右孩子在数组中的下标;

Parent保存结点的双亲结点在数组中的下标。

struct Element           //哈夫曼树结点的结构体

{  

char ch;                  //字符

int weight;               //结点权值

       intparent;          //双亲指针

       intlchild;           //左孩子指针

       intrchild;           //右孩子指针

};

示意图为:

weight

Lchild

Rchild

parent

(3)    设计一个结构体HCode保存编码表中各结点的信息,包括:

Data保存编码表结点的字符;

Code[100]保存编码表结点的编码。

struct HCode//编码表结构体

{

       chardata;//字符

       charcode[100];//编码内容

};

示意图为:

data

code[100]

 

算法设计

1.     系统模板划分

 《数据结构课程设计》题目3 哈夫曼算法的应用_第1张图片


2.      主要函数分析

(1)   创建哈夫曼树

a.用户输入需要编译的字符和该字符的权值(即其字母的频度)。

b.构造哈夫曼算法。设计一个数组H保存哈夫曼树中各结点的信息。

c.数组H初始化,将所有结点的孩子域和双亲域的值初始化为-1。

d.数组H的前n个元素的权值给定值。

e.调用select函数选择权值最小的根结点进行合并,其下标分别为i1,i2。

f.将二叉树i1,i2合并为一棵新的二叉树。

g.共进行n-1次合并,直到剩下一棵二叉树,这棵二叉树就是哈夫曼树。

 

函数实现:

int Select(Element H[],int i)       //选择两个最小的

{

       intmin=11000;

       int min1;

       for(intk=0;k

       {

              if(H[k].weight

              {

                     min=H[k].weight;

                  min1=k;

              }

       }

       H[min1].parent=1;

       returnmin1;

}

 

void HuffmanTree(Element H[],int w[],int n)  //构造哈夫曼树

{

       inti1=0,i2=0;

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

       {

              H[i].lchild=-1;

              H[i].parent=-1;

              H[i].rchild=-1;

       }

       for(intl=0;l

       {

              H[l].weight=w[l];

       }

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

       {

              inti1=Select(H,k);

              inti2=Select(H,k);

              if(i1>i2)

              {

                     inttemp;

                     temp=i1;

                     i1=i2;

                     i2=temp;

              }

              H[i1].parent=k;

              H[i2].parent=k;

              H[k].weight=H[i1].weight+H[i2].weight;

              H[k].lchild=i1;

              H[k].rchild=i2;

       }

}

 

(2)   创建编码表

a.      根据已经创建的哈夫曼树创建编码表。

b.      从叶子结点开始判断。

c.       如果当前叶子结点的双亲不是根结点,并且是其双亲的左孩子,则编码为‘0’,否则为‘1’。

d.      然后往上对其双亲进行判断,重复操作,直到每个字符编码完毕。

e.      将已经完成的编码调用reserve函数进行倒置。

f.       按照“下标n,权值weight,左孩子LChuld,右孩子RChild,双亲parent,字符char,编码code”的顺序输出编码表。

 

函数实现:

void Reverse(char c[])   //将字符串倒置

{

       int n=0;

       char temp;

       while(c[n+1]!='\0')

       {

              n++;

       }

       for (inti=0;i<=n/2;i++)

       {

              temp=c[i];

              c[i]=c[n-i];

              c[n-i]=temp;

       }

}

 

void CreateCodeTable(Element H[],HCode HC[],intn)      //输出哈弗曼编码表

{

   HC=newHCode[n];

   for (inti=0;i

   {

              HC[i].data=H[i].ch;

              int ic=i;

              int ip=H[i].parent;

              int k=0;

       while(ip!=-1)

        {

                if (ic==H[ip].lchild)   //左孩子标'0'

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

                else

                              HC[i].code[k]='1';  //右孩子标'1'

                k++;

                ic=ip;

                ip=H[ic].parent;

              }

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

     Reverse(HC[i].code);

   }

       cout<

              <

              <

              <

              <

              <

              <

              <

              <

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

       {

              if(i

              {

                     cout<

                            <

                            <

                            <

                            <

                            <

                            <

                            <

                            <

              }

              else

                     cout<

                         <

                         <

                            <

                            <

                            <

                            <

                            <

                            <

       }

}

 

(3)   解码

a.      用户输入要解码的二进制字符串,建立一个字符数组存储输入的二进制字符。

b.      创建一个指向待解码的字符串的第1个字符的指针。

c.       读取每一个字符。设置一个根结点的指针,从根结点开始判断。

d.      若字符为‘0’,则指向哈夫曼树当前结点的左孩子。

e.      若字符为‘1’,则指向当前结点的右孩子。

f.       直到指针指向的当前结点的左孩子为-1时,输出符合的字母。

g.      输出解码结果。

 

函数实现:

void Decode(Element H[],HCode HC[],int n,char *s)    //解码函数

{

       cout<<"解码数据为:";

       inti=2*(n-1);      //根结点

       while(*s!='\0')

       {

 

                     if(*s=='0')

                            i=H[i].lchild;

                     else

                            i=H[i].rchild;

                     if(H[i].lchild==-1)

                     {

                            cout<

                            i=2*n-2;

                     }

                     s++;

       }

       cout<

}

 

 

(4)   主函数执行流程

 《数据结构课程设计》题目3 哈夫曼算法的应用_第2张图片


时间复杂度和空间复杂度

1.       Select函数,时间复杂度O(n)

2.       Reverse函数,时间复杂度O(n)

3.       HuffmanTree函数,构造哈夫曼树,时间复杂度O(n!)

4.       CreateCodeTable函数,构造和输出哈夫曼编码表,时间复杂度O(n)

5.       Decode函数,解码,时间复杂度O(n)


源代码

#include
#include
#include
using namespace std;

struct Element
{
	char ch;
    int weight;
    int lchild, rchild, parent;
};

struct HCode
{
   char  data;
   char  code[100];  
};

int Select(Element H[],int i)       //选择两个最小的
{
	int min=11000;
	int min1;
	for(int k=0;ki2)
		{
			int temp;
			temp=i1;
			i1=i2;
			i2=temp;
		}
		H[i1].parent=k;
		H[i2].parent=k;
		H[k].weight=H[i1].weight+H[i2].weight;
		H[k].lchild=i1;
		H[k].rchild=i2;
	}
}

void CreateCodeTable(Element H[],HCode HC[],int n)      //输出哈弗曼编码表
{
   HC=new HCode[n];
   for (int i=0;i>select;
		if(select==0)  break;
		switch(select){
		case 1:
			{
			cout<>n;
			cout<>s;
				H[t].ch=s;
				HC[t].data=H[t].ch;
				cout<<"请输入该字符的权值:";
				cin>>e[t];
				cout<>s;
			Decode(H,HC,n,s);
			system("pause");
			break;
			}
		default:
			cout<<"没有此选项,请重新选择!"<


程序运行结果

程序运行时,首先出现主界面菜单,由用户进行功能选择,选择不同的数字,进入不同的功能区。

选择1,则进入输入编译字符集界面

《数据结构课程设计》题目3 哈夫曼算法的应用_第3张图片

选择2,进入编码表输出界面

《数据结构课程设计》题目3 哈夫曼算法的应用_第4张图片

选择3,进入解码界面

《数据结构课程设计》题目3 哈夫曼算法的应用_第5张图片


心得

通过本次课程设计实习,我从中受益匪浅,对数据结构这一门课程有了更深一步的认识,而且学会了如何应用哈夫曼算法编写一个简单的加密程序,利用哈夫曼树解决编译代码的问题,更加熟练掌握二叉树相关知识。在这次实习中,为了设计出一个系统性的程序,我查找了不少资料,把这学期和上学期C++所学的理论知识和实践联系起来,从而进一步加深对数据结构课程的理解和掌握,能把所学的知识融会贯通,做到与实践相结合,并且加深和巩固了所学的理论知识,提高实践能力和计算机的综合运用能力。在完完整整的做完一个课程设计后,我对程序的整体性也有了一个明确的认识,更加注意程序前后的联系,具有清晰的思路,确保程序具有层次。但是这次学习设计的应用程序只是简单的程序,因此我的学习还任重道远,在学习中要注重理论知识和实践能力的相结合,而且要系统性地整理知识。


参考文献

【1】《数据结构(C++版)(第2版)》,清华大学出版社,王红梅,胡明,王涛编著;

【2】《数据结构(C++版)(第2版)学习辅导与实验指导》,清华大学出版社,王红梅,胡明,王涛编著;

【3】《C++语言程序设计教程与实验(第三版)》,清华大学出版社,魏秀梅,丁学均,李建华编著;

【4】《北邮数据结构实验3哈夫曼编码》http://wenku.baidu.com/view/0ab9ae427fd5360cba1adb4e.html

 


你可能感兴趣的:(数据结构实验,数据结构,实验)