哈夫曼树(HuffmanTree)

1.编写程序任意输入结点个数、结点信息和结点权值,构造一棵哈夫曼树,生成哈夫曼编码序列,并验证是否正确。

采用不同的权值序列进行哈夫曼树的创建,检验其编码的正确性。

采用不同的选择方式生成相应的哈夫曼树及其编码。

#全代码在最后。

  • 第一题:

运行结果如下:

代码讲解:

1、存储结构

typedef struct{
	ElemType elem;
	int weight;
	int parent,lchild,rchild;
}HTNode,*HuffmanTree;

结合教材,也为方便,故采用顺序结构体数组;

elem 用于存放字符,weight用于存放权值;

parent、lchild、rchild 用于存放其父、左孩子、右孩子的位置

2、Huffman树的初始化前奏

typedef struct weight
{
	char elem; 
	int  weight1; 
}Weight; // 保存字符权值
	Weight *w;
	int n,i;
	printf("请输入Huffman树的结点个数:");
	scanf("%d",&n);
	w=(Weight *)malloc(n*sizeof(Weight));
	printf("\n注意,字符和权值之间需以空格作间隔\n\n");
	for(i=0;i<n;i++){
		printf("请输入第%d个结点的代码和其权值 : ",i+1);
		scanf("%ls%d",&w[i].elem,&w[i].weight1);
	}

用其他结构体来提前存储Huffman树的字符信息和权值,简单,防止混淆

3、构造Huffman树函数

// 构造huffman树
void CreateHuffmantree(HuffmanTree &HT,int n,Weight* &w){
	void OutPutHuffmanTree(HuffmanTree HT,int n);
	if(n<=1)return;
	int i,s1,s2;
	int m=2*n-1;
	HT=(HuffmanTree)malloc( (m+1) *sizeof(HTNode));// 从1开始
	for(i=1;i<=n;i++){
		HT[i].elem=w[i-1].elem;
		HT[i].weight=w[i-1].weight1;
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}
	for(i=n+1;i<=m;i++){
		HT[i].elem='-';
		HT[i].weight=0;
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}
	printf("HT树的初态:\n");
	OutPutHuffmanTree(HT,n);
	for(i=n+1;i<=m;i++){
		Select(HT,i-1,s1,s2);
		HT[s1].parent=i,HT[s2].parent=i;
		if(s1<s2){
			HT[i].lchild=s1;
			HT[i].rchild=s2;           
		}
		else{
			HT[i].lchild=s2;
			HT[i].rchild=s1;           
		}
        HT[i].weight=HT[s1].weight+HT[s2].weight;
	}
 
}

首先,开辟一段的HTNode型的空间,易知,N个结点需合并N-1次,故需2*n-1段空间,且由于0号位不用,故多开辟一段空间;

其次,开始初始化,将结构体数组 w 的值传入huffman树中,并将后面的树除字符信息外全设为0;

最后,进行合并。在合并循环中,利用Select函数选出前n个结点权值最小且未被选过(其parent=0)的两个,并将当前循环的i作为其父,选出较小的作为左孩子,较大的作为右孩子,直至循环完毕,由此生成Huffman树。

4、Select 函数:

void Select(HuffmanTree HT,int n,int &s1,int &s2){
	int min,i;
	// 首先选出第一个最小值
	for(i=1;i<=n;i++){
		if(HT[i].parent==0){
			min=i;
			break;
		}
	}
	for(i=min+1;i<=n;i++){
		if(HT[i].parent==0&&(HT[i].weight<HT[min].weight) ){
			min=i;
		}
	}
	s1=min;
	// 再选出第二个最小值
	for(i=1;i<=n;i++){
		if(HT[i].parent==0 && i!=s1){
			min=i;
			break;
		}
	}
	for(i=min+1;i<=n;i++){
		if(HT[i].parent==0 &&(HT[i].weight<HT[min].weight) && i!=s1){
			min=i;
		}
	}
	s2=min;
}

函数比较简单,首先选出一个未被选过(parent=0)的结点,然后逐一比较后面同样未被选过为结点,得到最小的结点;

然后,再选出一个未被选过的结点,同样比较,但结点位置宇上一结点位置不能相同。

5、Huffman编码函数:


void CreateHuffmanCode(HuffmanTree &HT,HuffmanCode &HC,int n){
	HC=(HuffmanCode)malloc(n*sizeof(char *) ); // 分配n个字符编码
	if(!HC)return;
	char *cd; // 作为临时存储编码的空间
	int i,c,f,start; // 临时变量
	cd = (char *)malloc(n*sizeof(char));
	cd[n-1] = '\0'; // 编码结束符,方便复制
	for(i=1;i<=n;i++){
        start=n-1;
		for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent){
			if(HT[f].lchild==c){cd[--start]='0';}
			else{cd[--start]='1';}
		}
		HC[i]=(char *)malloc( (n-start)*sizeof(char));
		strcpy(HC[i],&cd[start]);
	}
	free(cd);
}

该函数是从叶子逆向求每个字符的Huffman编码;

首先,分配n个字符编码的头指针向量,接着分配求编码的工作空间;

其次,在循环中,找起父节点,并以 HT[i].pareng!=0 为终止条件,如果,其为其父的左结点,则让cd[--start]=‘1’,否则为‘0’;其中,cd空间为倒叙输入,为方便输出,且不使用栈;

最后,为第i个字符编码开辟空间,并将已经编好的码赋值给它。

采用不同的权值序列进行哈夫曼树的创建,检验其编码的正确性。

如上,序列打乱,结果仍同,只是相应的,结果序列也被打乱了;

采用不同的选择方式生成相应的哈夫曼树及其编码。

如图,改变选择方式,将左右孩子变换,相应的,其编码为原来编码的反码。

2.(选做)结合模板中使用的存储结构,试着使用不同的存储形式来表示哈夫曼树及其编码。

模板应用说明

模板应用举例(结合教材图例6-2的数据,输入如下图左 ):

最后结果(如下图右显示):

  • 第二题

其实就是第一题的答案,所采用的存储形式恰好为例题6-2的形式,只是在此基础上多输出一个HT树的初态。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef char ElemType;

typedef struct{
	ElemType elem;
	int weight;
	int parent,lchild,rchild;
}HTNode,*HuffmanTree;

typedef char** HuffmanCode;


typedef struct weight
{
	char elem; 
	int  weight1; 
}Weight; // 保存字符权值
// select函数,用于选出最小的两值
void Select(HuffmanTree HT,int n,int &s1,int &s2){
	int min,i;
	// 首先选出第一个最小值
	for(i=1;i<=n;i++){
		if(HT[i].parent==0){
			min=i;
			break;
		}
	}
	for(i=min+1;i<=n;i++){
		if(HT[i].parent==0&&(HT[i].weight<HT[min].weight) ){
			min=i;
		}
	}
	s1=min;
	// 再选出第二个最小值
	for(i=1;i<=n;i++){
		if(HT[i].parent==0 && i!=s1){
			min=i;
			break;
		}
	}
	for(i=min+1;i<=n;i++){
		if(HT[i].parent==0 &&(HT[i].weight<HT[min].weight) && i!=s1){
			min=i;
		}
	}
	s2=min;
}

// 构造huffman树
void CreateHuffmantree(HuffmanTree &HT,int n,Weight* &w){
	void OutPutHuffmanTree(HuffmanTree HT,int n);
	if(n<=1)return;
	int i,s1,s2;
	int m=2*n-1;
	HT=(HuffmanTree)malloc( (m+1) *sizeof(HTNode));// 从1开始
	for(i=1;i<=n;i++){
		HT[i].elem=w[i-1].elem;
		HT[i].weight=w[i-1].weight1;
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}
	for(i=n+1;i<=m;i++){
		HT[i].elem='-';
		HT[i].weight=0;
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}
	printf("HT树的初态:\n");
	OutPutHuffmanTree(HT,n);
	for(i=n+1;i<=m;i++){
		Select(HT,i-1,s1,s2);
		HT[s1].parent=i,HT[s2].parent=i;
		if(s1<s2){
			HT[i].lchild=s1;
			HT[i].rchild=s2;           
		}
		else{
			HT[i].lchild=s2;
			HT[i].rchild=s1;           
		}
        HT[i].weight=HT[s1].weight+HT[s2].weight;
	}
 
}

void CreateHuffmanCode(HuffmanTree &HT,HuffmanCode &HC,int n){
	HC=(HuffmanCode)malloc(n*sizeof(char *) ); // 分配n个字符编码
	if(!HC)return;
	char *cd; // 作为临时存储编码的空间
	int i,c,f,start; // 临时变量
	cd = (char *)malloc(n*sizeof(char));
	cd[n-1] = '\0'; // 编码结束符,方便复制
	for(i=1;i<=n;i++){
        start=n-1;
		for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent){
			if(HT[f].lchild==c){cd[--start]='0';}
			else{cd[--start]='1';}
		}
		HC[i]=(char *)malloc( (n-start)*sizeof(char));
		strcpy(HC[i],&cd[start]);
	}
	free(cd);
}

// 输出Huffman树的权值
void OutPutHuffmanTree(HuffmanTree HT,int n){
	int j;
    printf("  结点  weight  parent  lchild  rchild");
	for (j=1; j<=2*n-1; j++){
		printf("\n%4d%8d%8d%8d%8d",j,HT[j].weight,
		HT[j].parent,HT[j].lchild, HT[j].rchild);
	}
	printf("\n");
}

// 输出Huffman树编码
void OutputHuffmanCode(HuffmanTree HT,HuffmanCode HC,int n) 
{ 
	int i; 
	printf("\n各代码的Huffman编码如下:\n"); 
	for(i=1;i<=n;i++) 
		printf("%c    %d 的编码:    %s\n",HT[i].elem,HT[i].weight,HC[i]); 
} 

int main()
{
	HuffmanTree HT;
	HuffmanCode HC;
	Weight *w;
	int n,i;
	printf("请输入Huffman树的结点个数:");
	scanf("%d",&n);
	w=(Weight *)malloc(n*sizeof(Weight));
	printf("\n注意,字符和权值之间需以空格作间隔\n\n");
	for(i=0;i<n;i++){
		printf("请输入第%d个结点的代码和其权值 : ",i+1);
		scanf("%ls%d",&w[i].elem,&w[i].weight1);
	}
    CreateHuffmantree(HT,n,w);
	CreateHuffmanCode(HT,HC,n);
	printf("HT树的末态:\n");
	OutPutHuffmanTree(HT,n);
	printf("\n");
	OutputHuffmanCode(HT,HC,n);
	return 0;
}

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值