哈夫曼编码

一.哈夫曼树

1.什么是哈夫曼树?

哈夫曼树又称最又树,是一类带权路径长度最短的树。

2.哈夫曼树的特点

哈夫曼树中权值越大的离根越近,权值越小的离根越远。

在哈夫曼树中只有度为2或者度为0的结点,没有度为1的结点,这与哈夫曼树的构造过程有关。

3.如何构造哈夫曼树

构造过程:(1):构造树林全是根

(2):选择两小造新树

(3):删除两小添新人

(4):重复(2)(3)只剩根

算法:

初始化:首先动态申请2n个单元;循环2n-1次,从1号单元开始,依次从1至2n-1所有单元的双亲,左右孩子,都初始化为0;最后循环n次,输入前n个单元中叶子结点的权值

创建树:循环n-1次,通过n-1次的选择,删除合并来创建哈夫曼树。选择是从树林中选择双亲为0且权值最小的两个树根结点s1和s2;删除是指将结点s1和s2的双亲改为非0;合并就是将s1和s2的权值和作为一个新结点的权值依次存入数组的n+1号及之后的单元中,同时记录这个新结点的左孩子下标为s1,右孩子下标为s2。

void CreatHuff(HuffmanTree **T,int n) 
{
	if(n<=1) return;
	
	int m = 2*n-1;//哈夫曼树的总结点数 
	*T = (HuffmanTree*)malloc(sizeof(HuffmanTree)*(m+1));//不用0号元素 
	
	for(int i = 1;i<=m;i++)
	{
		(*T)[i].lchild = (*T)[i].rchild = (*T)[i].parent = 0;//初始化 
	}//这里用数组访问元素应该用.因为传入的参数实际是数组名 并不是每一个使用的元素都是指针 只是为了传入数组 
	for(int i = 1;i<=n;i++)
	{
		int tmp = 0;
		scanf("%d",&tmp);
		//getchar();
		(*T)[i].weight = tmp;//给前n个结点权值 
	}
	for(int i = n+1;i<=m;i++)//i初始化为新结点的开始 
	{
		int p1,p2;
		p1 = p2 = 0;
		Select(*T,i,&p1,&p2);//p1 p2传回来的是最小权值 
		
		int s1,s2;
		s1 = s2 = 0;
		int flag = 0;
		for(int j = 1;j<=i-1;j++)
		{
			if((*T)[j].weight==p1&&(*T)[j].parent==0)
			{
				if(flag==0)//防止s1与s2输入的值一样 
				s1 = j;
				
				flag =1;
			}
			if((*T)[j].weight==p2&&(*T)[j].parent==0)
				s2 = j;
			if(s1!=0&&s2!=0&&s2!=s1)
				break;
		}//搜索权值最小的结点的序号 
		
		(*T)[i].weight = p1+p2;
		(*T)[i].lchild = s1;
		(*T)[i].rchild = s2;
		(*T)[s1].parent = i;
		(*T)[s2].parent = i; 
	}
	for(int i = 1;i<=m;i++)
	{
		printf("%d %d %d %d\n",(*T)[i].weight,(*T)[i].parent,(*T)[i].lchild,(*T)[i].rchild);//遍历一下看构建是否正确
	}
}

选择最小权值函数实现:

这里使用的是冒泡排序,时间复杂度为O(n*n)

void Select(HuffmanTree *T,int n,int *p1,int *p2)//p1,p2为挑选出来权值最小的两个结点 
{//用冒泡排序来选择 
	//首先把没有双亲的结点存储在另一个数组中 
	int array[n];
	int s = 0;
	for(int i = 1;i<=n-1;i++)
	{
		if(T[i].parent==0)
		{	
			array[s] = T[i].weight;
			s++;	
		}
	}
	if(s==0) return;
	for(int i = 0;i<s-1;i++)
	{
		for(int j = 0;j<s-1;j++)
		{
			if(array[j]>array[j+1])
			{
				int tmp = array[j];
				array[j] = array[j+1];
				array[j+1] = tmp;
			}
		}
	}
	*p1 = array[0];
	*p2 = array[1];
}

二.哈夫曼编码

在构建哈夫曼树后,求哈夫曼编码的主要思想是:依次以叶子结点为出发点,向上回溯至根结点为止。回溯时走左分支生成代码0,右分支生成代码1。

为什么哈夫曼编码能够保证字符编码总长最短?

因为哈夫曼树的带权路径最短,出现频率高的字符,编码最短。

代码实现如下:

void _CreatHuff(HuffmanTree *T,int n,HuffmanCode **TC)
{
	int m = 2*n-1;//总结点树
	*TC = (HuffmanCode*)malloc(sizeof(HuffmanCode)*(n+1));
	for(int i = 1;i<=n;i++)
	{
		(*TC)[i].weight = i;
		(*TC)[i].str = (char*)malloc(sizeof(char)*10);
	}
	for(int i = 1;i<=n;i++)
	{
		int j = i;
		int k = 0;
		int x = i;
		printf("第%d个结点的哈夫曼编码:",i);
		do{
			int tmp = j;
			for(int i = n+1;i<=m;i++)
			{
				if(T[i].lchild==tmp)
				{
					(*TC)[x].str[k]= 0;
					k++;
					break;
				}
				if(T[i].rchild==tmp)
				{
					(*TC)[x].str[k] = 1;
					k++;
					break;
				}
			}
			j = T[j].parent;
		}while(T[j].parent!=0);
		for(int i = k-1;i>=0;i--)//逆序输出 
		{
			printf("%d",(*TC)[x].str[i]);	
		} 
		putchar('\n');
	} 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值