实验三 线性表的基本操作及其应用
一、 实验目的
1、使学生熟练掌握哈夫曼树的生成算法。
2、熟练掌握哈夫曼编码的方法。
二、 实验内容和要求
【问题描述】
n堆果子, 每堆果子数量任意,试设计一种最佳方案,将这n堆果子合并为一堆,使得合并工作量最小。
注:规定合并两堆果子的工作量是这两堆果子的数量之和。
【标准输入】
M,N M表示M组测试数据,N表示每组测试数据数量不超过N个,每堆果子数量不超过10000。随后的M行是测试数据。
【标准输出】
M行数据表示对应果子的合并工作量
【输入样例】:
2 6
7, 5, 2, 4
5,6,2,9,7
【输出样例】:
35
65
三、 算法设计
1.本题使用的是哈夫曼编码的方法,使得果子数量越小的越早被合并。但每次都要找出数量最小的堆,每次都要遍历一遍时间复杂度比较高,所以使用了最小堆。
2.下面介绍最小堆和哈夫曼编码的过程:
样例:7、5、2、4
首先将这四个数字放入最小堆,数据存放如下:
7 5 5 2 2 2 额,这部分本来是有连线和圈圈的
5 7 7 2 7 5 7 5 4 5
4 7
把数据在最小堆中存放好,然后就可以进行哈夫曼编码,过程如下;
从最小堆中拿出一个数据,然后再拿出一个,两者生成一棵树,树的根节点的权值为两者之和,这两个数据分别为左右两个叶节点。不断重复上述过程,当堆中没有数据时则完成了哈夫曼编码:
提出2 : 7 4
4 5 7 5
提出4 : 5
7
将2和4组成一棵树,生成新的根节点,权值为两者之和6。
将6放入最小堆: 5
7 6
提出5 : 6
7
提出6 : 7
将5和6组成一棵树,生成新的根节点,权值为两者之和11。
将11放入最小堆: 7
11
提出7 : 11
提出11: 空
将7和11组成一棵树,生成新的根节点,权值为两者之和18。
因为对已经为空,所以结束操作,算出WPL。
18
7 11
5 6
2 4
WPL=18+11+6=35
四、 调试分析
很庆幸在整个过程中并没有出下过太严重的错误,可能是在一开始的设计上比较正确的原因,虽然完成的很顺利但是那个堆类并不能应用于所有的情境下,且成员函数比较少。
五、 实验结果
六、 总结
理解和实现中间有很大的差距,因此要多动手,多实现。
七、 源程序(带注释)
#include <iostream>
using namespace std;
typedef int elemtype;
struct node //定义一个树节点结构体
{
elemtype date; // 数据存放
node *r,*l,*p; // 分别是右儿子指针,左儿子指针,双亲指针
};
class minheap // 定义一个最小堆,用于排序,时间复杂度较低
{
public:
minheap()
{
length=0;
capcity=10;
p=new node[11];
}
~minheap()
{
delete []p;
}
void putin(node a)
{
if(length==capcity)
{
capcity=2*capcity;
node *s=new node[capcity+1];
for(int i=1;i<=length;i++)
s[i]=p[i];
delete []p;
p=s;
}
else
{
p[length+1]=a;
length++;
int k=length;
while(k/2>=1)
{
if(p[k/2].date<=p[k].date)
break;
else
{
node s1=p[k];
p[k]=p[k/2];
p[k/2]=s1;
}
k=k/2;
}
}
}
node putout()
{
node a=p[1];
p[1]=p[length];
length--;
int k=1;
node s1=p[1];
while((2*k<=length&&p[2*k].date<p[k].date)||(2*k+1<=length&&p[2*k+1].date<p[k].date))
{
if(2*k+1<=length)
{
if(p[2*k].date<p[2*k+1].date)
{
if(p[2*k].date<p[k].date)
{
p[k]=p[2*k];
p[2*k]=s1;
k=2*k;
}
else
break;
}
else
{
if(p[2*k+1].date<p[k].date)
{
p[k]=p[2*k+1];
p[2*k+1]=s1;
k=2*k+1;
}
else
break;
}
}
else
{
if(p[2*k].date<p[k].date)
{
p[k]=p[2*k];
p[2*k]=s1;
k=2*k;
}
else
break;
}
}
return a;
}
int getlength()
{
return length;
}
private:
int length;
int capcity;
node *p;
};
void haha(node *a) //后序遍历式的进行释放内存
{
if(a->l!=NULL)
haha(a->l);
if(a->r!=NULL)
haha(a->r);
//cout<<a->date<<endl;
delete a;
}
int main()
{
int m,n;
cin>>m>>n;
while(m)
{
node a,*head=NULL; //head指向哈夫曼树的根节点
minheap hp;
int sum=0;
for(int i=0;i<n;i++)
{
cin>>a.date;
if(a.date==0)
break;
a.l=NULL;
a.r=NULL;
a.p=NULL;
hp.putin(a);
}
if(hp.getlength()==0)
cout<<"wrong!"<<endl;
else if(hp.getlength()==1)
cout<<hp.putout().date<<endl;
else
{
node *p1=NULL,*p2=NULL,*p3=NULL;
while(hp.getlength())
{
p1=new node;
p2=new node;
p3=new node;
*p1=hp.putout();
*p2=hp.putout();
p3->date=p1->date+p2->date;
p1->p=p3;
p2->p=p3;
p3->l=p1;
p3->r=p2;
sum=sum+p3->date;
if(hp.getlength()==0)
{
head=p3;
break;
}
hp.putin(*p3);
}
cout<<sum<<endl;
}
haha(head);
m--;
}
return 0;
}