今天来写一份作业,是这周的ds实验作业
#include <iostream>
#include <string.h>
using namespace std;
typedef struct{
int weight;
int parent,lc,rc;
}HTnode,*HManTree;
typedef char**Huffmancode;
void Select(HManTree HT,int t,int &s1,int &s2);
首先把每个结点的结构定义出来,包括当前节点的权重,父节点,左子树和右子树的位置
Huffmancode用以存储每个叶子节点的haffman编码
声明Select函数
haffman树创建以及huffman编码生成的主要函数实现
void HuffmanCoding(HManTree&HT,Huffmancode&HC,int*w,int n)
HT是存放Huffman树的数组,HC是存Huffman编码,w传入权重,n为叶子节点个数
if(n<=1) return;
int m=2*n-1;
HT=new HTnode[m+1];
int i=1;
HManTree p=HT;
for(;i<=n;++i,++p,++w) //创建森林,用数组实现哈夫曼树
{
*p={*w,0,0,0};
cout<<p->weight<<endl;
}
for(i=n+1;i<=m;++i,++p) //除了前n个是森林,后续用来存放非叶子节点,每个都初始化为0
{
*p={0,0,0,0};
}
m=2*n-1,是整个Huffman树的节点总数,开出空间
进行n次循环,将前n个结点权重初始化,其余属性置为0
然后后续结点,四个属性均置为0
for(i=n;i<=m;++i) //填满从n+1到m的位置
{
int s1,s2;
Select(HT,i,s1,s2); //从当前结点上一结点的总表中挑选出最小的两个结点,构成新结点
HT[s1].parent=i; //更新两个结点的父节点
HT[s2].parent=i;
HT[i].lc=s1; //更新新节点的左子树和右子树
HT[i].rc=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight; //更新父节点的权重,是两子树之和
}//当循环结束,则哈夫曼树构建完成,根节点是HT[m]
HT[m].parent=0;
从HT[n]开始创建结点,每次创建时,从当前位置之前的权重中搜索到最小的两个值,分别作为左子树和右子树,select函数返回的s1,s2的值就是两个最小值的位置
将两个最小值位置的父节点置为当前结点,当前节点的左右子树改为这两个结点
当前结点的权重为两个最小值的和
将根结点的父节点置为0
HC=new char*[n+1]; //n+1个字符串
char*cd=new char[n]; //cd是长度为n的字符串
cd[n-1]='\0'; //cd的末尾是\0
for(int i=0;i<n;++i)
{
//前n个位置存放的是叶子节点,因此从这n个位子开始向父节点遍历
int start=n-1; //倒着填入哈夫曼编码
int c=i;
int f=HT[i].parent;
for(;f!=0;c=f,f=HT[f].parent)
{
if(HT[f].lc==c) cd[--start]='0'; //如果是父节点的左子树,就
else cd[--start]='1';
}
HC[i]=new char[n-start]; //HC是二维字符串,HC[i]代表前n个结点中的第i个结点作为叶子节点的哈夫曼编码,此步开出来
strcpy(HC[i],&cd[start]); //赋值
}
delete []cd;
申请空间存放Huffman编码,给HC
cd作为每次循环储存每个叶子结点的Huffman编码的存储器
从叶子结点开始逐个读取其父节点,如果检验完成是父节点的左子树,则置为0,如果是右子树,置为1
因为是从叶子节点开始向根结点回溯的,因此在记录Huffman编码的时候要倒着记录
为HC[i]开出[n-start]大小的空间(因为每次存一个Huffman编码,start都会减一)
然后存入HT[i]的Huffman编码
把用作临时存储器的cd释放掉
void Select(HManTree HT,int t,int &s1,int &s2)
{
int value1=1<<30;
int value2=1<<30;
for(int i=0;i<t;i++)
{
if(HT[i].parent!=0) continue;
if(value1>HT[i].weight)
{
if(value2>value1)
{
value2=value1;
s2=s1;
}
value1=HT[i].weight;
s1=i;
}
if(i!=s1)
{
if(value2>HT[i].weight)
{
value2=HT[i].weight;
s2=i;
}
}
}
}
之后是select函数的实现
在循环中如果检测到该节点已有父节点,则continue
要注意一点就是,如果用value1存放最小值,value2存放除最小值以外的最小值,在循环中会遇到,value1遇到了一个更小的值,把value1原本的值扔掉了,这时候需要检测value1要扔掉的值是否比value2要小,如果是,那么这个值不能扔掉,而应该给value2,替换掉value2的较大值
if(value1>HT[i].weight)
{
if(value2>value1)
{
value2=value1;
s2=s1;
}
value1=HT[i].weight;
s1=i;
}
就是这部分,检测value1扔掉的值是否小于value2的值
int main()
{
int nums[10]={2,4,2,1,3,2,6,9,7,8};
HManTree HT;
Huffmancode HC;
int a=sizeof(nums)/4;
HuffmanCoding(HT,HC,nums,a);
for(int i=0;i<a;++i)
{
cout<<HC[i]<<endl;
}
return 0;
}
测试数据
总的来说,借由数组实现Huffman树并输出Huffman编码不算太难,理清思路,注意select函数的实现,基本就没有问题