哈夫曼编码(数据结构树,c语言版)

哈夫曼编码

  • 一、实验题目
    • 1.数据结构
    • 2.算法
      • 2.1 构造哈夫曼树
      • 2.2 根据哈夫曼树求哈夫曼编码
  • 二、工具环境
  • 三、实验代码

一、实验题目

1)初始化。读入每个字符的权值,建立哈夫曼树HuffTree;
2)编码。用已建好的哈夫曼树,进行编码;
3)输出。显示建立的哈夫曼树和对应的编码表;

1.数据结构

//- - - - -哈夫曼树的存储表示- - - - -
typedef struct{ 
int weight; //结点的权值
int parent,lchild,rchild; //结点的双亲、左孩子、右孩子的下标
) HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树

2.算法

算法1 构造哈夫曼树
算法2 根据哈夫曼树求哈夫曼编码

2.1 构造哈夫曼树

算法步骤

构造哈夫曼树算法的实现可以分成两大部分。
1.初始化:首先动态申请2n个单元;然后循环2n-1次,从1号单元开始,依次将1至2n-1所有单元中的双亲、左孩子、右孩子的下标都初始化为0;最后再循环n次,输人前n个单元中叶子结点的权值。
2.创建树:循环n-1次,通过n-1次的选择、删除与合并来创建哈夫曼树。选择是从当前森林中选择双亲为0且权值最小的两个树根结点s1和s2;删除是指将结点s1和s2的双亲改为非0;合并就是将s1和s2的权值和作为一个新结点的权值依次存人到数组的第n +1之后的单元中,同时记录这个新结点左孩子的下标为s1,右孩子的下标为s2。

算法描述

void CreateHuffmanTree(HuffmanTree &HT,int n) 
{//构造哈夫曼树 HT
	if(n<=1) return; 
	m=2*n-1; 
	HT=new HTNode[m+1]; //0号单元未用,所以需要动态分配m+1个单元,HT[m]表示根结点
	for(i=1;i<=m;++i) //将1~m号单元中的双亲、左孩子、右孩子的下标都初始化为0
		{HT[i] .parent=O;HT[i] .lchild=O;HT[i] .rchild=O;} 
	for(i=l;i<=n;++i} //输人前n个单元中叶子结点的权值
		cin>>HT[i].weight; 
/*- ---- ----- -初始化工作结束, 下面开始创建哈夫曼树- - - - ------ */
	for (i=n+1; i<=m; ++i} 
	{//通过n-1次的选择、删除、合并来创建哈夫曼树
		Select (HT, i-1, sl, s2}; 
		//在 HT[k](1<=k<=i-1) 中选择两个其双亲域为0且权值最小的结点,并返回它们在HT中的序号s1和s2
		HT[s1].parent=i;HT[s2].parent=i; 
		//得到新结点i,从森林中删除sl,s2,将s1和s2的双亲域由0改为1.
		HT[i].lchild=s1;HT[i].rchild=s2; ///s1,s2分别作为i的左右孩子
		HT[i].weight=HT[s1].weight+HT[s2].weight; //i的权值为左右孩子权值之和
	}//for
}

2.2 根据哈夫曼树求哈夫曼编码

算法步骤

1.分配存储n个字符编码的编码表空间HC,长度为n+1;分配临时存储每个字符编码的动态数组空间cd,cd[n-1]置为\0’。

2.逐个求解n个字符的编码,循环n次,执行以下操作:

2.1设置变量start用于记录编码在cd中存放的位置,start初始时指向最后,即编码结束符位置n-1;
2.2设置变量c用于记录从叶子结点向上回溯至根结点所经过的结点下标,c初始时为当前待编码字符的下标i,f用于记录i的双亲结点的下标;
2.3从叶子结点向上回溯至根结点,求得字符i的编码,当f没有到达根结点时,循环执行以下操作:

2.3.1回溯一次start向前指一个位置,即–start;
2.3.2若结点c是f的左孩子,则生成代码0,否则生成代码1,生成的代码0或1保存在cd[start]中;
2.3.3继续向上回溯,改变c和f的值。

2.4根据数组cd的字符串长度为第i个字符编码分配空间HC[ i],然后将数组cd 中的编码
复制到HC[ i]中。

3.释放临时空间cd。

算法描述

void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n) 
{//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
	HC=new char* [n+1]; //分配存储n个字符编码的编码表空间
	cd=new char [n]; //分配临时存放每个字符编码的动态数组空间
	cd[n-1]='\0'; //编码结束符
	for(i=1;i<=n;++i) //逐个字符求哈夫曼编码
	{
		start=n-1; //start 开始时指向最后,即编码结束符位置
		c=i; f=HT[i].parent; //f指向结点c的双亲结点
		while(f!=O) //从叶子结点开始向上回溯,直到根结点
		{
			--start; //回溯一次start向前指一个位置
			if(HT[f].lchild==c) cd[start]='O'; //结点c是f的左孩子,则生成代码0
			else cd[start]='1'; //结点c是f的右孩子,则生成代码1
			c=f;f=HT[f].parent; //继续向上回溯
		} //求出第i个字符的编码
		HC[i]=new char[n-start]; //为第i个字符编码分配空间
		strcpy(HC[i],&cd[start]); //将求得的编码从临时空间cd复制到HC的当前行中
	}//for 
		delete cd; //释放临时空间
}

二、工具环境

Window10操作系统,Microsoft Visual C++2010学习版 集成开发环境,C语言

三、实验代码

#include
#include
#include

typedef struct{
	int weight; //结点的权值
	int parent,lchild,rchild; //结点的双亲、左孩子、右孩子的下标
} HTNode,*HuffmanTree; 
typedef char **HuffmanCode;

void Select(HuffmanTree HT,int n,int *s1,int *s2);
void CreateHuffmanTree(HuffmanTree *HT,int n,int number[]);
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode *HC,int n);
void ShowResults(HuffmanTree HT,HuffmanCode HC,int n);

int main()
{
	HuffmanTree HT=NULL;
	HuffmanCode HC=NULL;
	int n=8,number[8]={5,29,7,8,14,23,3,11};//此处number存储的是权值,读者可以自己写读入权值和数量的初始化函数
	CreateHuffmanTree(&HT,n,number);
	CreateHuffmanCode(HT,&HC,n);
	ShowResults(HT,HC,n);
	return 0;
}

void Select(HuffmanTree HT,int n,int *s1,int *s2)
{//选择两个其双亲域为0且权值最小的结点,并返回它们在HT中的序号sl和s2
	int num[100],index[100],i,j,k=0,max=0,temp;
	for(i=1;i<=n;i++)
	{
		if(HT[i].parent==0)
		{//双亲域为0
			k++;
			num[k]=HT[i].weight;
			index[k]=i;	//记录双亲域为0结点的序号和权值
		}
	}
	for(i=1;i<k;i++)
	{//排列出最小的两个结点
		for(j=i+1;j<=k;j++)
		{
			if(num[i]>num[j])
			{
				temp=num[i];
				num[i]=num[j];
				num[j]=temp;
				temp=index[i];
				index[i]=index[j];
				index[j]=temp;//序号和权值同时交换
			}
		}
	}
	*s1=index[1];//将最小权值的两个结点序号返还给s1,s2
	*s2=index[2];
}

void CreateHuffmanTree(HuffmanTree *HT,int n,int number[])
{//构造哈夫曼树
	int m=2*n-1;
	int i,s1=1,s2=1;
	if(n<=1)  return; 
    *HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //0号单元未用,所以需要动态分配m+l个单元,HT[m)表示根结点
	for(i=1;i<=m;++i)    
	{//将l~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
		(*HT)[i].parent=0;
	    (*HT)[i].lchild=0;
		(*HT)[i].rchild=0;
	} 
	for(i=1;i<=n;++i) 
    {//输入前n个单元中叶子结点的权值
		(*HT)[i].weight=number[i-1]; 
	}
	//- ----------初始化工作结束,下面开始创建哈夫曼树---- ------
	for ( i=n+1; i<=m; ++i)
	{//通过n-1次的选择、删除、合并来创建哈夫曼树
		Select(*HT, i-1, &s1, &s2); 
	//在HT[k](l<=k<=i-1)中选择两个其双亲域为0且权值最小的结点,并返回它们在HT中的序号sl和s2
		(*HT)[s1].parent=i;
		(*HT)[s2].parent=i; 
	//得到新结点i, 从森林中删除sl,s2, 将sl和s2的双亲域由0改为i
		(*HT)[i].lchild=s1;
		(*HT)[i].rchild=s2; 
	//sl, s2分别作为i的左右孩子
		(*HT)[i].weight=(*HT)[s1].weight+(*HT)[s2].weight; //i的权值为左右孩子权值之和
    }//for	
} 

void CreateHuffmanCode(HuffmanTree HT,HuffmanCode *HC,int n) 
{//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
	int i,start,c,f;
    char *cd;
	*HC=(char **)malloc((n+1)*sizeof(char *));  //分配存储n个字符编码的编码表空间
	cd=(char *)malloc(n*sizeof(char)); //分配临时存放每个字符编码的动态数组空间
	cd[n-1]='\0';  //编码结束符
	for(i=1;i<=n;++i)     //逐个字符求哈夫曼编码
	{
		start=n-1;        //start开始时指向最后, 即编码结束符位置
        c=i; 
		f=HT[i].parent; //f指向结点c的双亲结点
		while(f!=0)       //从叶子结点开始向上回溯,直到根结点
		{
			--start;//回溯一次start向前指一个位置
			if(HT[f].lchild==c) cd[start]='0'; //结点c是f的左孩子,则生成代码0
			else cd[start]='1'; //结点c是f的右孩子,则生成代码1
			c=f;
			f=HT[f].parent; //继续向上回溯
		}                       //求出第i个字符的编码
		(*HC)[i]=(char *)malloc((n-start)*sizeof(char)); //为第i个字符编码分配空间
		strcpy((*HC)[i],&cd[start]); //将求得的编码从临时空间cd复制到HC的当前行中
	}//for 
	cd=NULL;   //置空指针
	free(cd);  //释放临时空间
}

void ShowResults(HuffmanTree HT,HuffmanCode HC,int n)
{//展示创建的哈夫曼树和对应的哈夫曼编码
	int i;
	printf("\n\t结点i\tweight\tparent\tlchild\trchild\n");
	for(i=1;i<=2*n-1;++i) 
    {
		printf("\t%d\t%d\t%d\t%d\t%d\n",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild); 
	}
	printf("\n\n\n\t结点i\tHuffmanCode\n");
	for(i=1;i<=n;++i)     
	{
		printf("\t%d\t%s\n",i,HC[i]);
	}
}

你可能感兴趣的:(数据结构,c语言,数据结构,霍夫曼树,c语言)