哈夫曼编码

思路:
利用结构体数组,结构体中存放parent(双亲),weight(权值),lchild(左孩子),rchild(右孩子);注:0位置不使用。

初始化哈夫曼树

首先我们从主函数中传入n个叶子结点的权值(也就是我们存放的n个数值,存放数组传递),初始化哈夫曼树时,我们需要将n个叶子结点的权值分别赋值,parent、lchild、rchild全部赋值为0;

因为我们存放n个字符,结构体数组的0位置还不使用,总的数据结点总数本该为2*n-1,现在数组的大小需要+1。

我们使用1~ n存放叶子结点,n+1 ~ 2n存放非叶子结点,非叶子结点四个参数初始时全部赋值为0;

我们利用循环操作,查找出结构体数组中两个权值最小的两个,将两个权值相加得到新的权值,然后改变数组n位置之后的权值(最小两权值的和),lchild,rchild分别存放两个最小权值的位置,(所以我们写的选择函数需要返回两个最小权值的结点的位置),改变两个最小权值结点的parent,将新得到的位置进行赋值操作;

代码实现:

void init_huffmantree(HuffmanTree ht, int w[], int n) //因为0单位不使用,所以需要建立ht[M+1],使用w[]存放n个权重值; 
{
	int i;
	for(i=1; i<=n; i++)  //1~n存放叶子节点 
	{
		ht[i].weight = w[i-1];
		ht[i].parent = 0;
		ht[i].lchild = 0;
		ht[i].rchild = 0;	
	} 
	int m = 2 * n - 1; 
	for(i=n+1; i<=m ;i++)  //n+1~m用来存放非叶子节点 
	{
		ht[i].weight = 0;
		ht[i].parent = 0;
		ht[i].lchild = 0;
		ht[i].rchild = 0;
	}
	int s1,s2;
	for(i=n+1; i<=m; i++)
	{
		Select(ht, i-1, &s1, &s2);
		ht[i].weight = ht[s1].weight + ht[s2].weight;
		ht[i].lchild = s1;
		ht[i].rchild = s2;
		ht[s1].parent = i;
		ht[s2].parent = i;
	}
}

编写选择函数

我们需要选择结构体数组中1~最新的到的结点之间的权值(weight)中挑选两个最小的【大前提是必须是parent==0的结点,因为parent!=0结点都已经有了双亲】,所以需要四个参数【1.数组,2.遍历循环挑选的次数,3.返回的第一个序号,4.返回的第二个序号】

代码实现:

void Select(HuffmanTree ht, int n, int *s1, int *s2)//n表示叶子节点数,因为需要返回两个最小权重值的序号,所以需要两个指针用来接收返回; 
{
	int i, min1 = MAX, min2 = MAX;
	*s1 = 0;
	*s2 = 0;
	for(i=1;i<=n;i++)
	{
		if(ht[i].parent == 0)
		{
			if(ht[i].weight < min1)
			{
				min2 = min1;
				*s2 = *s1;
				min1 = ht[i].weight;
				*s1 = i;
			}
			else if(ht[i].weight < min2)
			{
				min2 = ht[i].weight;
				*s2 = i;
			}
		}
	}
		
} 

最后便是存码,printf 输出(相当于解码)

由上至下,我们使用结点位于一层左节点一次,添加一个0,反之右节点添加一个1;

该函数的参数有三个【1.结构体数组,2.由主函数传入的存放字符的数组,3.最大哈夫曼树的深度】,因为我使用的是每一个字符的密码使用一个数组进行存放,解码相当于遍历输出数组,所以需要第三个参数限制数组的遍历的次数,此方法需要每次进行清空数组操作;

这里我是用倒推哈夫曼树,每一次循环都从叶子节点推至根节点

代码实现:

void create_huffmantree(HuffmanTree ht, char s[], int n)  //s[]用来接收main函数传入的字符,这里我们使用变长编码; 
{
	char *HFM = (char *)malloc(sizeof(char) * n);
	for(int i=1;i<=n;i++)
	{
		int start = n-1, c = i, p = ht[i].parent;
		while(p != 0)
		{
			--start;
			if(ht[p].lchild == c)
			{
				HFM[start] = '0';	//左孩子为0 
			}
			else
			{
				HFM[start] = '1';//右孩子为1	
			}
			c = p;					
			p = ht[p].parent;	//向上到推 
		}
		printf(" %c 的哈夫曼编码为:", s[i-1]);
		for(int j=0; j<n; j++) //输出哈夫曼编码 
		{
			if(HFM[j] == '0' || HFM[j] == '1')
			{
				printf("%c",HFM[j]);	
			}	
		}
		printf("\n");
		memset(HFM, -1, n);  //每一次都清空数组	
	} 
}

全部代码:

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

#define N 30 //定义最大叶子节点个数
#define M 2 * N - 1  //定义结点的最大值
#define MAX 999999

typedef struct{
	
	int parent;
	int weight;
	int lchild;
	int rchild;
}HTNode,HuffmanTree[M+1];  //HuffmanTree是结构体数组,0单位不用 

 

void Select(HuffmanTree ht, int n, int *s1, int *s2)//n表示叶子节点数,因为需要返回两个最小权重值的序号,所以需要两个指针用来接收返回; 
{
	int i, min1 = MAX, min2 = MAX;
	*s1 = 0;
	*s2 = 0;
	for(i=1;i<=n;i++)
	{
		if(ht[i].parent == 0)
		{
			if(ht[i].weight < min1)
			{
				min2 = min1;
				*s2 = *s1;
				min1 = ht[i].weight;
				*s1 = i;
			}
			else if(ht[i].weight < min2)
			{
				min2 = ht[i].weight;
				*s2 = i;
			}
		}
	}
		
} 


//哈夫曼树初始化操作 ,建立哈夫曼编码 
void init_huffmantree(HuffmanTree ht, int w[], int n) //因为0单位不使用,所以需要建立ht[M+1],使用w[]存放n个权重值; 
{
	int i;
	for(i=1; i<=n; i++)  //1~n存放叶子节点 
	{
		ht[i].weight = w[i-1];
		ht[i].parent = 0;
		ht[i].lchild = 0;
		ht[i].rchild = 0;	
	} 
	int m = 2 * n - 1; 
	for(i=n+1; i<=m ;i++)  //n+1~m用来存放非叶子节点 
	{
		ht[i].weight = 0;
		ht[i].parent = 0;
		ht[i].lchild = 0;
		ht[i].rchild = 0;
	}
	int s1,s2;
	for(i=n+1; i<=m; i++)
	{
		Select(ht, i-1, &s1, &s2);
		ht[i].weight = ht[s1].weight + ht[s2].weight;
		ht[i].lchild = s1;
		ht[i].rchild = s2;
		ht[s1].parent = i;
		ht[s2].parent = i;
	}
}

void create_huffmantree(HuffmanTree ht, char s[], int n)  //s[]用来接收main函数传入的字符,这里我们使用变长编码; 
{
	char *HFM = (char *)malloc(sizeof(char) * n);
	for(int i=1;i<=n;i++)
	{
		int start = n-1, c = i, p = ht[i].parent;
		while(p != 0)
		{
			--start;
			if(ht[p].lchild == c)
			{
				HFM[start] = '0';	//左孩子为0 
			}
			else
			{
				HFM[start] = '1';//右孩子为1	
			}
			c = p;					
			p = ht[p].parent;	//向上到推 
		}
		printf(" %c 的哈夫曼编码为:", s[i-1]);
		for(int j=0; j<n; j++) //输出哈夫曼编码 
		{
			if(HFM[j] == '0' || HFM[j] == '1')
			{
				printf("%c",HFM[j]);	
			}	
		}
		printf("\n");
		memset(HFM, -1, n);  //每一次都清空数组	
	} 
}


int main(){
	
	HuffmanTree ht;
	int i,w[4] = {1, 2, 3, 4}; //权值 
	char str[4] = {'c', 'h', 'e', 'n'};// 叶子节点的字符 
	init_huffmantree(ht, w, 4);
	printf("哈夫曼树的所有结点值:\n"); 
	for(i=1;i<=7;i++)
	{
		printf("%d ", ht[i].weight);
	}
	printf("\n");
	create_huffmantree(ht, str, 4);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值