数据结构实验二哈夫曼树及哈夫曼编码译码的实现

时间:2024.5.9

福建农林大学金山学院实验报告

系(教研室): 专业: 计算机科学与技术 年级: 08 实验课程: 姓名: 学号:

实验室号:_______ 计算机号:

实验时间: 指导教师签字: 成绩: 实验二:哈夫曼树及哈夫曼编码译码的实现(验证性、4学时)

一、 实验目的和要求

构建哈夫曼树及哈夫曼编码,输出哈夫曼树及哈夫曼编码,完成编码与译码的

算法。

(1)掌握树的有关操作算法

(2)熟悉树的基本存储方法

(3)学习利用树求解实际问题

二、 实验内容和原理

定义哈夫曼树的存储结构;输入要编码的字符权重,根据权重建立哈夫曼

树,并进行编码,最后输出哈夫曼编码。

三、 实验环境

硬件:(1)学生用微机(2)多媒体教室或远程教学(3)局域网环境

软件:(1)Windows XP中文操作系统 (2)Turbo C 3.0

四、 算法描述及实验步骤

1. 算法描述

(1).建立哈夫曼树的算法

定义各节点类型其中应包含两类数据

一是权重域weight;一是指针域而指针域中应该包括指向左右孩子

和指向双亲的指针这里分别用lchild、rdhild和parent来表示因此可

用静态三叉链表来实现,在实际构造中由于是叶子节点来构造新的

根节点其构造过程中仅与叶子节点的权重有关而与其数据域无关所

以构造过程中不用考虑其数值域,并且在链表中从叶子开始存放,

让后不断的将两颗最小权值的子树合并为一颗权值为其和的较大的

子树,逐步生成各自内部节点直到树根。

(2).哈夫曼编码的算法

将建立的哈夫曼树从每个叶子节点开始沿着双亲域回到根节点,梅走一步进行编码得到一位编码值;由于每个叶子节点的哈夫曼编码是从根节点到相应的叶子的路径的各个分支的代码组成的0和1序列,所以先得到了低位编码后得到高位编码因此可用一维数组从后向前来存放各位编码值,并用start来记录编码的起始位置。

2. 算法流程图

构建哈夫曼树算法流程

数据结构实验二哈夫曼树及哈夫曼编码译码的实现

哈夫曼编码算法流程

数据结构实验二哈夫曼树及哈夫曼编码译码的实现

3. 代码

#include <stdio.h>

#include <malloc.h>

#define maxvalue 10000 //定义最大权值常量

#define maxnodenumber 100 //定义节点最大数

#define maxbit 10 //定义哈弗曼编码最大长度

typedef struct //定义新数据类型即节点结构

{int weight; //权重域

int parent,lchild,rchild; //指针域

}htnode; //节点类型标识符

//typedef htnode * huffmanstree; //定义哈弗曼数类型

htnode ht[maxnodenumber]; //定义三叉链表存储数组

typedef struct //定义保存一个叶子节点哈弗曼编码的结构

{

int bit[maxbit]; //定义一维数组为编码域 int start; //定义位置域

}hcnodetype; //定义编码类型

htnode * creatstree(int n) //huffmanstree creatstree(int n) //建立哈夫曼树算法实现函

数 { int i,j,m1,m2,k1,k2; //局部变量 for(i=0;i<2*n-1;i++) //初始化各节点 { } for(i=0;i<n;i++) //权重赋初值,由用户输入 { } for(i=0;i<n-1;i++) //生成新节点构造哈夫曼树 { m1=maxvalue; //预置最小权值变量为最大权值 m2=maxvalue; //预置次小权值变量为最大权值 k1=0; //预置最小权值节点位置为下标为0处 k2=0; //预置次小权值节点位置为下标为0处 for(j=0;j<n+i;j++) //循环找出每趟最下权值和所在位置 if(ht[j].parent==-1&&ht[j].weight<m1) { } else //当小于当前次小m2则更新m2及其位置 if(ht[j].parent==-1&&ht[j].weight<m2) m2=m1; k2=k1; m1=ht[j].weight; k1=j; scanf("%d",&ht[i].weight); ht[i].weight=0; //权重初始化为0 ht[i].parent=-1; //根节点和给左右孩子初始化为-1 ht[i].lchild=-1; ht[i].rchild=-1;

{ } m2=ht[j].weight;k2=j; ht[k1].parent=n+i; //修改最小权值节点的双亲为刚生成的新节点 ht[k2].parent=n+i; //修改次小权值节点的双亲为刚生成的新节点 ht[n+i].weight=ht[k1].weight+ht[k2].weight; //将新生成的权重值填入新的根节点

}

void getstree(htnode * ht,int n) //哈夫曼编码算法及打印函数的实现

{

} ht[n+i].lchild=k1; //新生节点左孩子指向k1 ht[n+i].rchild=k2; //新生节点右孩子指向k2 return ht; //返回哈夫曼树指针 int i,j,c,p; //局部变量的定义 hcnodetype cd[maxnodenumber]; //定义存储哈夫曼编码的数组 for(i=0;i<n;i++) //循环控制对每一个节点进行编码 { c=i; //为编码各节点初始化c和j j=maxbit; do { j--; //j指向bit中存放编码为的正确位置 p=ht[c].parent; //p指向c的双亲节点 if(ht[p].lchild==c) //如果c是p的左孩子 cd[i].bit[j]=0; //编码为赋值0 else //否则即c是p的右孩子 cd[i].bit[j]=1; //编码赋值1 c=p;//更新当前指针,为下一节点编码做准备

}

} } }while(ht[p].parent!=-1); //判断是否编码结束即循环至最终根节点 cd[i].start=j; //编码完成,记下编码开始位置 for(i=0;i<n;i++) //循环打印各节点哈夫曼编码 { for(j=cd[i].start;j<maxbit;j++)//循环逐一输出 printf("%d",cd[i].bit[j]); printf("\n"); //每输出一编码后换行

int main() //主函数

{

int n;

printf("请输入节点数:"); //用户输入节点数

scanf("%d",&n);

htnode * p; // huffmanstree p //定义哈夫曼树类型p

p=(htnode * )malloc(sizeof(htnode *));//p=(huffmanstree)malloc(sizeof(huffmanstree))//分配内存空间

p=creatstree(n);//调用建立哈夫曼树函数赋返回值给p

getstree(p,n); //调用编码函数读入建立的哈夫曼树p进行编码

return 0;

}

五、 调试过程

数据结构实验二哈夫曼树及哈夫曼编码译码的实现

出现该错误是因为type识别不了,即定义哈夫曼树时确切的说是type并不能定义htnode *标识符为huffmanstree:type htnode * huffmanstree

这个小错误可以通过连个方法来修改一是将type改为typedef,当然直接删除该定义完全不会影响程序的执行,但在定义建立哈夫曼树函数时返回值应直接用

htnode *;

数据结构实验二哈夫曼树及哈夫曼编码译码的实现

该错原因是参数未能成功传递,其中的ht[p]系统当做是未定义的类型可知,在getstree(htnode ht,int n)时正确的应当是传递哈夫曼树的头指针即数组首地址ht因此改为getstree(htnode * ht,int n)

六、 实验结果

通过改正后成功编译连接,进行数据测试

{5,20,12,7,47,9}当然编码因为定义时大小的左右排序是不同的所以编码也不唯一,但在这里是以左小右大来分布的,所以编码结果符合预期的。

所构造哈夫曼树如图所示

七、 总结

该实验不仅让我加深了对哈夫曼树的理解,更是让我惊叹该算法的强大,一个简单的数组类型数据加之简单的链表在实现该算法后让我理解了基础理论知识的重要性,倘若在不知道这个算法的情况下。我不知道自己得花多少的时间来实现编码,或许最终还不一定能够很好的实现编码,更别说优化了。尤其树的基本存储方法更加深了我对树的理解。

数据结构实验二哈夫曼树及哈夫曼编码译码的实现

数据结构实验二哈夫曼树及哈夫曼编码译码的实现

附录:


第二篇:树的应用 哈夫曼编编码 和 译码


华%%%%%%%%%%%%%%%%%%学院  数据结构实验报告

20112012学年  学期   2011  计算机 专业

班级:           学号:               姓名:               

实验三  树的应用

一、实验题目:

树的应用——哈夫曼编/译码

二、实验内容:

利用哈夫曼编码进行通信可以大大提高信道的利用率,缩短信息传输的时间,降低传输成本。根据哈夫曼编码的原理,编写一个程序,在用户输入字符及权值的基础上求哈夫曼编码。要求:

(1)  从键盘输入字符集(字母a~z,空格)共27个字符,以及出现的频率,将字符出现的频率作为结点的权值,建立哈夫曼树,并输出数组ht[]的初态和终态。

(2)  对各个字符进行哈夫曼编码,打印输出字符及对应的哈夫曼编码。

(3)  编码:从键盘输入字符串,利用已建好的哈夫曼编码,实现该字符串的编码。

(4)  (选作)译码:从键盘输入二进制串,利用已建好的哈夫曼编码,将二进制串还原为字符串。

三、程序源代码:

  typedef struct

{

    char data;

    int weight;

    int parent;

    int lchild;

    int rchild;

}HTNode;

typedef struct

{

    char cd[100];

    int start;

}HCode;

//这里保存字母对应的编码,我本来想用指向字符数组的指针数组,可是后来想到利用结构体更简单。

struct Codes

{

    char ch;

    char codes[27];

};

#include<iostream.h>

#include<stdio.h>

#include<string.h>

const int maxsize=100;

//特色,动态编码

void tongji(char str[],int *pinlv);

void createHT(HTNode *ht,int n,int pinlv[]);

void showHT(HTNode ht[],int n);

void createHcode(HTNode ht[],HCode* hcd,int n);

void showHCode(HCode hcd[],int n,int pinlv[]);

//使字符与编码对应

void matchcodes(HCode hcd[],int pinlv[],int n,Codes* code);

void charToCode(Codes codes[],char *str);

void codeToChar(Codes codes[]);

void main()

{

    cout<<"本例实现动态编码:根据输入的字符串建立编码规则,然后按此规则对输入的字符串进行编码,对输入的编码进行译码操作"<<endl;

    //输入

    cout<<"input a string"<<endl;

    char str[maxsize];

    gets(str); 

    //统计

    int pinlv[27];

    int len=0;

    for(int i=0;i<27;i++)

        pinlv[i]=0;

    tongji(str,pinlv);

    for(int k=0;k<27;k++)

        if(pinlv[k]!=0)

            len++;

        cout<<len<<endl;

        //  cout<<pinlv[26]<<endl;

       

        //构造哈夫曼树

        HTNode ht[199];

        createHT(ht,len,pinlv);

        //哈夫曼编码

        HCode hcd[27];

        createHcode(ht,hcd,len);

        showHCode(hcd,len,pinlv);

        //字符与编码对应匹配

        Codes codes[27];

        matchcodes(hcd,pinlv,len,codes);

        //char to code

        charToCode(codes,str);

        // code to char

        codeToChar(codes);

}

//这个函数有错误,已经改正

void codeToChar(Codes codes[])

{

    cout<<"根据上面输出的编码规则,请输入要译码的01编码(相邻编码要以逗号分割,以“#”结束)"<<endl;

    char str[100];

    gets(str);

    cout<<str<<"的译码为:"<<endl;

    char temp[27]; //保存每个字符的编码,每次要赋 0 啊

    int i,j;

    for(i=0,j=0;i<100;i++)

    {

        if(str[i]!=',')

        {

            temp[j]=str[i];

            j++;

        }

        else

        {

            temp[j]='\0';

            for(int k=0;k<27;k++)

            {

                if(strcmp(codes[k].codes ,temp)==0)

                {

                    cout<<codes[k].ch <<" ";

                    //cout.flush();

                    break;

                }

            }

            j=0;  //赋0 操作

           

        }

        if(str[i]=='#')

        {

            break;

        }

       

    }

    cout<<endl;

}

void charToCode(Codes codes[],char *str)

{

    char ch=*str;

    int k=0;

    cout<<str<<"的编码为:"<<endl;

    while(ch!='\0')

    {

        for(int i=0;i<27;i++)

        {

            if(codes[i].ch ==ch)

                cout<<codes[i].codes<<",";

        }

        k++;

        ch=*(str+k);

       

    }

    cout<<endl;

}

//已经改进过的地方

void matchcodes(HCode hcd[],int pinlv[],int n,Codes* codes)

{

    int i,k,m;

    char ch='a';

    int p=0;

    char temp[27];

    for(int z=0;z<26;z++)

    {

        temp[z]=' ';

    }

    temp[26]='\0';

   

   

    for(i=0;i<27;i++)

    {

        if(pinlv[i]!=0)

        {

            ch='a';

            ch=char(ch+i);

            if(ch>='a'&&ch<='z')

            {

                codes[p].ch =ch;

                //测试

                /*  if(codes[p].ch==ch)

                {

                cout<<"succss"<<endl;

            }*/

            }

           

            else

                codes[p].ch =' ';

            m=0;

           

            for(k=hcd[p].start;k<=n;k++)

            {

                temp[m]=hcd[p].cd [k];

                m++;

            }

            //字符串必须给出结束符位置,否则会输出乱码啊

            temp[m]='\0';

           

            //codes[p]=temp;

            strcpy(codes[p].codes ,temp);

            //  cout<<codes[p].ch;

           

            //  cout<<codes[p].ch<<"-----"<<codes[p].codes<<endl;

            p++;

           

        }  

    }

}

void showHCode(HCode hcd[],int n,int pinlv[])

{

    int i,k;

    char ch='a';

    int p=0;

    cout<<"字符"<<" "<<"对应编码"<<endl;

    for(i=0;i<27;i++)

    {

        //每次必须从字符'a'开始

        ch='a';

        ////

        ch=char(ch+i);

        if(pinlv[i]!=0)

        {

           

            if(ch>='a'&&ch<='z')

                cout<<ch<<"    ";

            else

                cout<<" "<<"    ";

            for(k=hcd[p].start;k<=n;k++)

                cout<<hcd[p].cd [k];

            p++;

            cout<<endl;

        }

       

    }

   

}

void createHcode(HTNode ht[],HCode* hcd,int n)

{

    int i,f,c;

    HCode hc;

    for(i=0;i<n;i++)

    {

        //不是书上的    hc.start =n;

        hc.start =n-1;

        c=i;

        f=ht[i].parent ;

        while(f!=-1)

        {

            if(ht[f].lchild ==c)

                hc.cd [hc.start --]='0';

            else

                hc.cd [hc.start --]='1';

            c=f;

            f=ht[f].parent ;

        }

        //最后一位必须赋值为结束符

        hc.cd[n]='\0';

       

        hc.start ++;

        hcd[i]=hc;

       

    }

}

void createHT(HTNode* ht,int n,int pinlv[])

{

    for(int m=0;m<=2*n-1;m++)//初始化节点的所有与域值

        ht[m].parent =ht[m].lchild =ht[m].rchild =-1;

   

    char ch='a';

    int p=0;

    for(int z=0;z<27;z++)//循环27个字母(a-z和''),若频数大于0,就创建节点

    {  

        if(pinlv[z]!=0)

        {

            if(ch>='a'&&'z'>=ch)

            {

                ht[p].data =ch;

                ht[p].weight =pinlv[ch-97];

               

            }

            else

            {

                ht[p].data =' ';

                ht[p].weight =pinlv[26];

            }

            p++;

        }

       

        ch=char (ch+1);

    }

   

    cout<<"ht[]初态输出 ";

    showHT(ht,n);

    int i,k,lnode,rnode;

    int min1,min2;

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

    {

        min1=min2=32767;

        lnode=rnode=-1;

        for(k=0;k<=i-1;k++)

        {

            if(ht[k].parent ==-1)

            {

                if(ht[k].weight <min1)

                {

                    min2=min1;rnode=lnode;

                    min1=ht[k].weight ;lnode=k;

                }

                else if(ht[k].weight <min2)

                {

                    min2=ht[k].weight ;

                    rnode=k;

                }

            }

        }

        ht[lnode].parent =i;ht[rnode].parent =i;

        ht[i].weight =ht[lnode].weight +ht[rnode].weight ;

        ht[i].lchild =lnode;ht[i].rchild =rnode;

        ht[i].data ='*';

       

    }  

    cout<<"ht[]终态输出 ";

    showHT(ht,2*n-1);

}

void tongji(char str[],int *pinlv)

{

    char ch=*str;

    int k=1;

   

    while(ch!='\0')

    {   if(ch>='a'&&'z'>=ch)

            pinlv[ch-97]+=1;

        else

            pinlv[26]+=1;

        ch=*(str+k);

        k++;

    }

}

void showHT(HTNode ht[],int n)

{   cout<<"节点信息如下:"<<endl;

    cout<<"data"<<" "<<"weig"<<" "<<"pare"<<" "<<"lchd"<<" "<<"rchd"<<endl;

    for(int i=0;i<n;i++)

    {

        cout<<ht[i].data <<"      "<<ht[i].weight <<"    "<<ht[i].parent <<"   "<<ht[i].lchild <<"   "<<ht[i].rchild <<endl;

    }

}

四、 测试结果:

五、小结(包括收获、心得体会、存在的问题及解决问题的方法、建议等)

注:内容一律使用宋体五号字,单倍行间距

   通过本次实验,我感觉到自己的程序编程细节问题必须注意:如使用gets()函数可接收带有空格的输入字符串;在进行编码译码时,必须注意,数组的上下界问题。

  做一个程序,不仅要有思路,更要有细心,耐心。

更多相关推荐:
数据结构试验心得

数据结构课程设计心得体会(专业:计算机科学与技术姓名:朱文学号:20xx220xx7)通讯录管理系统是基于双向循环链表设计而成的信息管理系统。该系统通过对程序进行模块化,建立添加、显示、查找和删除功能的函数,各…

数据结构实训总结

这次课程设计的心得体会通过实习我的收获如下1、巩固和加深了对数据结构的理解,提高综合运用本课程所学知识的能力。2、培养了我选用参考书,查阅手册及文献资料的能力。培养独立思考,深入研究,分析问题、解决问题的能力。…

数据结构实验报告及心得体会

20XX~20XX第一学期数据结构实验报告班级:信管一班学号:*********姓名:***实验报告题目及要求一、实验题目设某班级有M(6)名学生,本学期共开设N(3)门课程,要求实现并修改如下程序(算法)。1…

数据结构与算法实验学期总结

20xx20xx学年第2学期数据结构与算法实验学期论文数据结构与算法实验学期总结我的数据结构班级09计本一班学号20xx810020姓名吴伟摘要数据结构实验的目的是为了加深对课堂知识的理解培养实验者的动手能力和...

数据结构实验报告

管理学院实验报告实验课程名称数据结构与算法实验地点经济管理教学实验中心年月至年月专业班级学生姓名学号指导老师13456789101112141516

数据结构综合实验心得体会

心得体会:做了一个星期的程序设计终于做完了,在这次程序设计课中,真是让我获益匪浅。对大一学习的C语言和这学期开的数据结构,并没有掌握,很多知识都不太懂,突然让自己独立完成一个程序让我手忙脚乱,起码在我认为那真的…

数据结构实验———图实验报告

数据结构实验报告目的要求掌握图的存储思想及其存储实现掌握图的深度广度优先遍历算法思想及其程序实现掌握图的常见应用算法的思想及其程序实现实验内容键盘输入数据建立一个有向图的邻接表输出该邻接表3在有向图的邻接表的基...

数据结构实验报告

数据结构实验报告学号1410122583姓名王佳斌诚信声明签字实验一实验题目数据集合的表示及运算实验目的构造一个非递减数列然后在该数列中新加入一个数并保持该数列非递减有序性的特征用一维数组存储数列实验中遇到的问...

数据结构实验报告

实验报告课程数学号姓名据结构实验日期20xx114实验指导老师胡青实验1链表的插入和删除一实验目的1了解单链表循环链表和双链表的基本知识2掌握算法思想和数据结构的描述3掌握链表的插入删除的相关语句及基本方法二实...

数据结构实验报告

实验报告一约瑟夫环问题1问题描述设编号为12nngt0个人按顺时针方向围坐一圈每人持有一个正整数密码开始时任意给出一个报数上限m从第一个人开始顺时针方向自1起顺序报数报到m时停止报数报m的人出列将他的密码作为新...

数据结构实验报告七

云南大学软件学院数据结构实验报告本实验项目方案受教育部人才培养模式创新实验区X3108005项目资助实验难度ABC学期20xx秋季学期任课教师秦江龙实验题目哈希表查找小组长联系电话147xxxxxxxx电子邮件...

数据结构实验报告一

数据结构实验报告实验题目班级学号姓名完成日期一需求分析1问题描述ProblemDescription给定两个不超过10000位的非负整数A和B计算出AB的值要求计算并输出AB的值2根据实验任务的要求分析大整数要...

数据结构实验总结(44篇)