哈夫曼树的构建.docx
- 文档编号:27743170
- 上传时间:2023-07-04
- 格式:DOCX
- 页数:19
- 大小:80.88KB
哈夫曼树的构建.docx
《哈夫曼树的构建.docx》由会员分享,可在线阅读,更多相关《哈夫曼树的构建.docx(19页珍藏版)》请在冰豆网上搜索。
哈夫曼树的构建
哈夫曼树的建立
学院计算机科学与技术
专业
学号
学生姓名
指导教师姓名
2011年12月29日
一、程序设计的内容与目的
内容:
1.哈夫曼树也就是最优二叉树,构建带有权值的二叉树是本程序的核心数据结构,使用链表实现二叉树节点信息的存储,链表中的结点结构如下:
typedefstructnode
{
elemdata;//二叉树节点信息
intweight;//二叉树的节点的权重
structnode*rChild;//二叉树节点的左孩子
structnode*lChild;//二叉树节点的右孩子
structnode*next;//连接二叉树节点的指针
}BinaryTreeNode;
2.程序应具有以下基本功能:
1.由键盘输入各个节点,并建立二叉树,能实现对二叉树的查询、插入、删除操作,构造其哈夫曼树。
2.使用文件进行存储和管理。
程序启动时可从文件中读取信息,或从键盘输入信息,可先建立二叉树,然后构造其哈夫曼树,有兴趣的同学可以将树直接画在显示屏幕上。
3.不同的功能使用不同的函数实现(模块化),对每个函数的功能和调用接口要注释清楚。
对程序其它部分也进行必要的注释。
6.通过命令行相应选项能直接进入某个相应菜单选项的功能模块。
目的:
数据结构课程设计是计算机专业重要的教学环节,它为学生提供了一个既动手又动脑,将课本上的理论知识和实际有机的结合起来,独立分析和解决实际问题的机会。
(1)进一步巩固和复习数据结构的基础知识。
(2)培养学生结构化程序、模块化程序设计的方法和能力。
(3)提高学生调试程序的技巧和软件设计的能力。
(4)提高学生分析问题、解决问题以及综合利用C语言进行程序设计的能力。
(5)了解软件的编制过程。
二、算法的基本思想
这是一个简单的哈夫曼树建立的程序,由键盘输入二叉树各个节点的信息(包括节点的权重)实现对二叉树的查询、插入、删除操作,并且可以构造其相应的哈夫曼树。
本程序使用了结构体来存放学生信息。
将学生的信息以链表的形式存储在内存中。
typedefstructnode
{
elemdata;
intweight;
structnode*rChild;
structnode*lChild;
structnode*next;
}BinaryTreeNode;
程序运行前先创建二叉树,创建完成后显示刚才创建的信息,然后根据需要进行删除二叉树节点,插入节点和对二叉树进行查找并且构造其对应的哈夫曼树等汇总操作。
创建二叉树的函数如下:
BinaryTreeNode*CreateBinaryTree(int*sum)
{
elemdata;
intweight;
BinaryTreeNode*root=(BinaryTreeNode*)malloc(sizeof(BinaryTreeNode*));
root=NULL;
printf("请输入二叉树的节点值和权重节点值为-1时结束\n");
getchar();
printf("请输入二叉树的节点值(整形):
");
scanf("%d",&data);
while(data!
=-1)
{
printf("输入节点的权值:
");
scanf("%d",&weight);
root=InsertNode(root,data,weight);
printf("请输入二叉树的节点值(整形):
");
scanf("%d",&data);
}
returnroot;
}
构建二叉树节点信息时必须输入权值,以便后面生成哈夫曼树。
当输入二叉树的节点值和权重节点值为-1时结束输入。
二叉树构建完毕,下面可以对二叉树进行一系列操作:
打印二叉树、删除二叉树节点、插入节点、查找节点、生成哈夫曼树、保存二叉树、读取二叉树等汇总操作。
打印二叉树的函数:
voidprintBinary(BinaryTreeNode*root)
{
if(root!
=NULL)
{
if(root->lChild!
=NULL)
{
printf("节点值:
%d:
权值:
%d:
",root->data,root->weight);
printf("左孩子节点值是:
%d,权值是:
%d\n",root->lChild->data,root->lChild->weight);
}
else
{
printf("节点值:
%d:
权值:
%d:
",root->data,root->weight);
printf("左孩子节点值是:
空,权值是:
空\n");
}
printBinary(root->lChild);
if(root->rChild!
=NULL)
{
printf("节点值:
%d:
权值:
%d:
",root->data,root->weight);
printf("右孩子节点值是:
%d,权值是:
%d\n",root->rChild->data,root->rChild->weight);
}
else
{
printf("节点值:
%d:
权值:
%d:
",root->data,root->weight);
printf("右孩子节点值是:
空,权值是:
空\n");
}
printBinary(root->rChild);
}
}
插入节点的函数:
BinaryTreeNode*InsertNode(BinaryTreeNode*tptr,elemkey,intweight)
{
BinaryTreeNode*f=NULL,*p=NULL;//p的初值指向根结点
p=tptr;
while(p)//查找插入位置,循环结束时,p是空指针,f指向待插入结点的双亲
{
if(p->data==key)//树中已有key,无须插入
returntptr;
f=p;//f保存当前查找的结点,即f是p的双亲
p=(key
p->lChild:
p->rChild;
}
p=(BinaryTreeNode*)malloc(sizeof(BinaryTreeNode*));//生成新结点
p->data=key;
p->weight=weight;
p->lChild=p->rChild=NULL;
if(tptr==NULL)//原树为空,新插入的结点为新的根
{
tptr=p;
}
else
{
if(key
{
f->lChild=p;
}
else
{
f->rChild=p;
}
}
returntptr;
}
删除节点的函数:
BinaryTreeNode*DeleteNode(BinaryTreeNode*tptr,elemkey)//删除
{
BinaryTreeNode*p=NULL,*tmp=NULL,*parent=NULL;
p=tptr;
while(p)
{
if(p->data==key)
break;
parent=p;
p=(key
p->lChild:
p->rChild;
}
if(!
p)returnNULL;
tmp=p;
if(!
p->rChild&&!
p->lChild)/*p的左右子树都为空*/
{
if(!
parent)//要删根,须修改根指针
tptr=NULL;
elseif(p==parent->rChild)
parent->rChild=NULL;
else
parent->lChild=NULL;
}
elseif(!
p->rChild)//p的右子树为空,则重接p的左子树
{
p=p->rChild;
if(!
parent)//要删根,须修改根指针
tptr=p;
elseif(tmp==parent->lChild)
parent->lChild=p;
else
parent->rChild=p;
}
elseif(!
p->lChild)//的左子树为空,则重接p的左子树
{
p=p->rChild;
if(!
parent)//要删根,须修改根指针
tptr=p;
elseif(tmp==parent->lChild)
parent->lChild=p;
else
parent->rChild=p;
}
elseif(p->rChild&&p->lChild)//p有左子树和右子树,用p的后继覆盖p然后删去后继
{//另有方法:
用p的前驱覆盖p然后删去前驱||合并p的左右子树
parent=p;//由于用覆盖法删根,则不必特殊考虑删根
p=p->rChild;
while(p->lChild)
{
parent=p;
p=p->lChild;
}
tmp->data=p->data;
if(p==parent->lChild)
parent->lChild=NULL;
else
parent->rChild=NULL;
}
returntptr;
}
查找节点的函数:
BinaryTreeNode*FindNode(BinaryTreeNode*root,elemdata)
{
BinaryTreeNode*l=NULL;
BinaryTreeNode*r=NULL;
if(root!
=NULL)
{
if(data==root->data)
{
returnroot;
}
l=FindNode(root->lChild,data);
r=FindNode(root->rChild,data);
}
if(l!
=NULL)
{
returnl;
}
elseif(r!
=NULL)
{
returnr;
}
else
{
returnNULL;
}
}
函数的重点当然是把建造处理的二叉树构造成其相对应的哈夫曼树了。
其函数如下:
while(head.next)
{
BinaryTreeNode*pN1=NULL,*pN2=NULL,*pRoot=NULL;
if(!
head.next->next)//只剩最后一个结点,这就是根
{
returnhead.next;
}
//取前两个出来构造一棵新树(因为链表已经按权值升序排列了,前两个就是最小的两个)
pN1=head.next;//第一个结点
pN2=pN1->next;//第二个结点
head.next=pN2->next;//将这两个结点从链表中删除
pRoot=(BinaryTreeNode*)malloc(sizeof(BinaryTreeNode*));//为这两个结点建立根结点
pRoot->weight=pN1->weight+pN2->weight;//权值相加
pRoot->data=pN1->data+pN2->data;
pRoot->lChild=pN1;//左分支为第一个结点
pRoot->rChild=pN2;//右分支为第二个结点
//将新的根加入哈夫曼森林
AddNode(&head,pRoot);
}
returnNULL;
}
其要调用以下几个函数:
voidGetAllBinaryInfo(BinaryTreeNode*root)
{
k++;
if(root==NULL)
{
data[k]=0;
power[k]=0;
}
else
{
data[k]=root->data;
power[k]=root->weight;
GetBinaryInfo(root->lChild);
GetBinaryInfo(root->rChild);
}
}
//加载所有节点信息
voidGetBinaryInfo(BinaryTreeNode*root)
{
if(root!
=NULL)
{
k++;
data[k]=root->data;
power[k]=root->weight;
GetBinaryInfo(root->lChild);
GetBinaryInfo(root->rChild);
}
}
voidGetNumber(BinaryTreeNode*root)
{
if(root!
=NULL)
{
sum++;
GetNumber(root->lChild);
GetNumber(root->rChild);
}
}
//给Huffman森林(按各树根结点的值升序排列的带头结点的链表)添加一棵Huffman树
//使得链表仍然有序
voidAddNode(BinaryTreeNode*headNode,BinaryTreeNode*pNode)
{
BinaryTreeNode*p=headNode;
//找到第一个权值小于给定结点的结点,p指定该结点的前一个结点
while(p->next&&p->next->weight
p=p->next;
if(p->next!
=NULL)
{
pNode->next=p->next;
}
else
{
pNode->next=NULL;
}
p->next=pNode;
}
//功能:
构造哈夫曼树
//参数说明
//n:
字符的个数
//datas:
要统计的字符
//powers:
字符对应的权值(出现的次数)
BinaryTreeNode*CreateHuffmanTree(intn,intdatas[],intpowers[])
{
BinaryTreeNodehead;
inti;
head.next=NULL;//初始化森林
for(i=0;i { BinaryTreeNode*pNode=(BinaryTreeNode*)malloc(sizeof(BinaryTreeNode*)); pNode->data=datas[i]; pNode->weight=powers[i]; pNode->lChild=pNode->rChild=pNode->next=NULL; AddNode(&head,pNode); } 三、 主要功能模块流程图 保存和读取 建立 打印 删除 查找 转化成哈夫曼树示 四、系统测试 程序运行实例如下: 1.选择1,运行界面如下: 2.选择2,运行界面如下: 3.选择3,运行界面如下: 4.选择4,运行界面如下: 5.选择5,运行界面如下: 6.选择6,运行界面如下: 7.选择7,运行界面如下: 8: 选择8和9程序运行正常 五、结论 通过这次长达两个星期的程序设计,我受益匪浅。 深刻认识到自己知识的不牢固以及编程实践能力的低下。 自学能力和自我认识都有一定的增强,也对编程有更深刻的认识。 在做这个程序的时候,我遇到了不少问题。 1、通过实验更好的掌握了哈夫曼树,并对哈夫曼树有了深一步的了解 2、更进一步掌握了有关类的操作 3、由于算法推敲不足,使程序调试时费时不少 4、本程序有些代码重复出现,从而减少了空间的利用率和增加了程序代码的杂乱性 5、更熟悉了文件的操作 6、更好的掌握了对程序的调试,并从中感受到调试的巨大的力量,特别是当程序不能实现预想结果时 总之,课程设计考察的是一个学生理论联系实际并真正理解知识的能力,在这次考察中,我收获了很多,也使我对C语言的理解运用有一定的提高。 很期待能在下一次的设计中有更好的表现。 希望老师能多多开展这样的课程。 参考文献: 1朱昌杰、肖建于编著,数据结构(C语言版),北京: 清华大学出版社 2谭浩强著,C程序设计(第三版),北京: 清华大学出版社
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 哈夫曼树 构建