实现哈夫曼树和哈夫曼编码

原谅小编做了只鸽子,鸽了这么久😅

哈夫曼树在日常生活中可以用于文件的压缩,所以是我们程序员必不可少的基本功,下面跟着小编我一起来实现哈夫曼树和编码吧!

一.哈夫曼树的实现

(一).实现原理

哈夫曼树是一种特殊的二叉树

我们先假设有一个森林,里面是一堆数据,有大有小,杂乱无章(类似数组)

哈夫曼树是先取森林中最小的两个值放入两个节点(值称为权值),权值相加放入另一个节点中(双亲节点),双亲的左右子树存放这两个子树的下标,两个子树的双亲存放双亲节点的下标。

注意:哈夫曼树没有用指针的方式指向双亲/孩子,而是用存放下标的方式进行结构连接,是因为之后编写哈夫曼编码时需要这样。

连接完成后,这三棵树再放入森林中,我们再从森林中取两个最小值进行上述步骤的重复,直到哈夫曼树编满。

不看图都是空谈:

哎,那有朋友要问了,怎么才算编满呢?

------我们是通过先计算森林的大小,确定哈夫曼树的大小的。

假设森林的大小为n,即有n个数据,那哈夫曼树中就有n个叶子节点,由图知有n-1个双亲结点,共有2*n-1个节点,但我们为了方便,会将下标为1的作为哈夫曼树的第一个节点,所以共创建2*n个节点,(不要小看这一步,这很重要),之后便是正式创建了!

(二).代码实现

我们这里分为两步

1.创建哈夫曼树

void CreatHuffmanTree(HuffmanTree** HT, HuffmanTreeType* CF, const int num)//CF: 森林;num:森林大小
{
	*HT = (HuffmanTree*)malloc(sizeof(HuffmanTree) * (num * 2));//先为哈夫曼树开辟空间
	for (int i = 1; i <= num; i++)//进行叶子节点的初始化
	{
		(*HT)[i].weight  = CF[i - 1];
		(*HT)[i].left = 0;
		(*HT)[i].right = 0;
		(*HT)[i].parent = 0;
	}
	for (int i = num + 1; i < num * 2; i++)//经行双亲结点的初始化
	{
		(*HT)[i].weight = 0;
		(*HT)[i].left = 0;
		(*HT)[i].right = 0;
		(*HT)[i].parent = 0;
	}
	//为哈夫曼树分配值
	for (int i = num + 1; i < num * 2; i++)
	{
		int l1 = 0, l2 = 0;
		Select_MinLeaf(*HT, i, &l1, &l2);
        //选择此时最小的两个节点,c++可以用直接引用,注意:这里要用i,因为i是此时所有已被赋值的节点的边缘
		(*HT)[l1].parent = i;//以下是进行叶子与双亲的连接
		(*HT)[l2].parent = i;
		(*HT)[i].left = l1;
		(*HT)[i].right = l2;
		(*HT)[i].weight = (*HT)[l1].weight + (*HT)[l2].weight;//双亲权值是叶子权值之和
	}
}

 2.选择此时哈夫曼树中最小的两个值

void Select_MinLeaf(HuffmanTree* HT, const int num, int* l1, int* l2)
//注意这里的HT是此时的哈夫曼树,不一定只有叶子节点,可能有已经创建好的双亲节点
{
	int minnum = 0;
    //找第一个最小值
	for (int i = 1; i < num; i++)//先找一个没有双亲节点的作为最小值
	{
		if (HT[i].parent == 0)
		{
			minnum = i;
			break;
		}
	}
	for (int i = 1; i < num; i++)//遍历一遍哈夫曼树,找到最小值
	{
		if (HT[i].parent == 0)
		{
			if (HT[i].weight < HT[minnum].weight) minnum = i;
		}
	}

	*l1 = minnum;
	minnum = 0;
	//找第二个最小值
	for (int i = 1; i < num; i++)
	{
		if (HT[i].parent == 0 && i != *l1)
		{
			minnum = i;
			break;
		}
	}
	for (int i = 1; i < num; i++)
	{
		if (i != *l1 && HT[i].parent == 0)
		{
			if (HT[i].weight < HT[minnum].weight) minnum = i;
		}
	}
	*l2 = minnum;
}

全部代码: 

#include<stdio.h>
#include<stdlib.h>
typedef int HuffmanTreeType;//定义哈夫曼树的类型

typedef struct HuffmanTree
{
	HuffmanTreeType left, right, weight, parent;
}HuffmanTree;
 
int CreatForce(HuffmanTreeType** a);
void CreatHuffmanTree(HuffmanTree** HT, HuffmanTreeType* CF, const int num);
void Select_MinLeaf(HuffmanTree* HT, const int num, int* l1, int* l2);
void PrintHuffmanTree(HuffmanTree* HT, const int num);
int main()
{
	HuffmanTree* HT;
	int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };//森林
	int num = sizeof(a) / sizeof(int);
	//int num = CreatForce(&a); //已经手动创建森林了
	CreatHuffmanTree(&HT, a, num);
	PrintHuffmanTree(HT, num*2);
	return 0;
}
int CreatForce(HuffmanTreeType** a) //返回森林大小
{
	int num = 0;
	printf("Please input the str's size:");
	scanf("%d", &num);
	*a = (HuffmanTreeType*)malloc(sizeof(HuffmanTree) * num);
	if (!num)
	{
		printf("Num error!\n");
		return;
	}
	for (int i = 0; i < num; i++)
	{
		printf("Please input the str[%d]'s num:", i);
		scanf("%d", &(*a)[i]);
	}
	return num;
}
void CreatHuffmanTree(HuffmanTree** HT, HuffmanTreeType* CF, const int num)//建树
	*HT = (HuffmanTree*)malloc(sizeof(HuffmanTree) * (num * 2));
	for (int i = 1; i <= num; i++)
	{
		(*HT)[i].weight  = CF[i - 1];
		(*HT)[i].left = 0;
		(*HT)[i].right = 0;
		(*HT)[i].parent = 0;
	}
	for (int i = num + 1; i < num * 2; i++)
	{
		(*HT)[i].weight = 0;
		(*HT)[i].left = 0;
		(*HT)[i].right = 0;
		(*HT)[i].parent = 0;
	}
	for (int i = num + 1; i < num * 2; i++)
	{
		int l1 = 0, l2 = 0;
		Select_MinLeaf(*HT, i, &l1, &l2);
		(*HT)[l1].parent = i;
		(*HT)[l2].parent = i;
		(*HT)[i].left = l1;
		(*HT)[i].right = l2;
		(*HT)[i].weight = (*HT)[l1].weight + (*HT)[l2].weight;
	}
}
void Select_MinLeaf(HuffmanTree* HT, const int num, int* l1, int* l2)//找树中最小两个值
{
	int minnum = 0;
	for (int i = 1; i < num; i++)
	{
		if (HT[i].parent == 0)
		{
			minnum = i;
			break;
		}
	}
	for (int i = 1; i < num; i++)
	{
		if (HT[i].parent == 0)
		{
			if (HT[i].weight < HT[minnum].weight) minnum = i;
		}
	}

	*l1 = minnum;
	minnum = 0;
	for (int i = 1; i < num; i++)
	{
		if (HT[i].parent == 0 && i != *l1)
		{
			minnum = i;
			break;
		}
	}
	for (int i = 1; i < num; i++)
	{
		if (i != *l1 && HT[i].parent == 0)
		{
			if (HT[i].weight < HT[minnum].weight) minnum = i;
		}
	}
	*l2 = minnum;
}
void PrintHuffmanTree(HuffmanTree* HT, const int num)//打印树
{
	for (int i = num / 2 + 1; i < num; i++)
	{
		printf("parent:%d  childleft:%d  childright:%d\n", HT[i].weight, HT[HT[i].left].weight, HT[HT[i].right].weight);
	}
}

 二.哈夫曼编码的实现(c++)

哈夫曼编码用于通过解压的过程,通过根节点找到叶子节点,可理解为找叶子的路径

(一).实现原理

创建一个字符型指针数组,每一个指针都是存放一个叶子节点的哈夫曼编码。

先找叶子节点,是双亲的左子树就放0,右子树就放1(在字符串中从后往前放),之后双亲作为叶子节点往上重复走,直到走到头节点,此时得到的字符串就是一个哈夫曼编码,根据这个编码就能找到叶子节点。

上图:

至于怎么确定哈夫曼编码的长度,我们可以设每个字符串长为数据总数(确保够长),设一个下标int border = n,每放一个数字border就减1,之后让 n - border就是哈夫曼编码的长度。

(二).代码实现

//typedef int* HuffmanCodeType ; HC:所有叶子节点哈夫曼编码组成的指针数组
void HuffmanCode(HuffmanTree* tr, HuffmanCodeType* HC, int n)//0为走左子树, 1为走右子树
//形参HuffmanCodeType* HC是二级指针,因为实参是指针数组,相当于二级指针
{

	for (int i = 1; i <= n; i++)//对每个叶子节点求编码
	{
		int j = i;
		int start = n;
		HuffmanCodeType tmp = (HuffmanCodeType)new char[n + 1];
        //创建一个新的变量放入一个叶子的编码,之后再放入HC中
		tmp[n] = '\0';
		while (tr[j].parent != 0)//从叶子节点开始溯源直到树顶
		{
			start--;
			int parent = tr[j].parent;
			if (tr[parent].LeftTree == j) tmp[start] = '0';//判断是否为左子树
			else tmp[start] = '1';//否则是右子树
			j = parent;
		}
		//注意:HC这里是二级指针,*(HC + i)相当于取主函数HC中的HC[i],HC[i]也是一个指针,对这个指针分配内存空间
		*(HC + i) = (HuffmanCodeType) new char[n - start];
		strcpy(*(HC + i), &tmp[start]);
	}
}

呕心沥血创作不易, 麻烦铁铁们点个赞,如有错误,敬请斧正😬

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

就要 宅在家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值