哈夫曼树以及应用

哈夫曼树结构的实现

存储对象:存储某段信息里每个信息的权值,权值存储在树的叶子节点。

节点之间的关系:

1:每个非叶子节点的权值等于左节点与右节点的权值之和。

2:除叶子节点外,所有节点的左节点的权值都小于右节点。

3:   除叶子节点外,每个节点的度都为2。

特点:权值越大的节点离根节点就越近。

对关系的建模:

1:采用结构体数组表示哈夫曼树。

2:结构体元素为该节点权值以及该节点双亲节点,左节点,右节点在指针数组的位置。

对关系的约束:

1:指针数组的长度为总结点个数+1

合法操作:

根据(权值)来从根节点开始创造树

根据哈夫曼树读取哈夫曼编码

根据权值创建哈夫曼树+实现哈夫曼编码

创建哈夫曼树

step1: 创建结构体数组,并初始化叶子节点和非叶子节点

step2: 找到两个根节点权值最小的二叉树,作为新树的左树和右树生成新树

step3: 删除这两个选中的树,并把新树加入森林。

step4: 重复step2和step3直到森林里只有一棵树

哈夫曼编码实现:

step1:构建二维字符数组存储哈夫曼编码

step2:从叶子节点走到根节点,子节点是双亲节点的左节点就记录0,右节点就为1,逆序记录。

#include<iostream>
#include<string.h>

using std::cin;
using std::cout;
using std::endl;

typedef char** HuffmanCode;

typedef class 
{
public:

	unsigned int weight;//节点权重
	unsigned int parent, left, right;//双亲节点,左节点,右节点在指针数组里的位置
}HNode, *HuffmanTree;

/*
p_HT为结构体指针,s1,s2是没有父节点的节点中最小权值的两个节点的位置,且s1小于s2,end为存放节点的前一个位置
该函数的目的是在还未有父节点的节点中,找到两个权值最小的节点,并确定它们的位置
*/
void SelectMin(HuffmanTree p_HT, int* s11, int* s22, int end)
{
	int m1, m2, index1, index2;
	HuffmanTree p = p_HT;
	m1 = 10000; m2 = 10000;
	index1 = 0; index2 = 0;


	for (int i = 1; i <= end; i++)
	{
		if ((p + i)->parent != 0) { continue; }
		else if ((p + i)->weight < m1)
		{
			m2 = m1;//假设m1小于m2
			m1 = (p + i)->weight;
			index2 = index1;
			index1 = i;
		}
		else if ((p + i)->weight <= m2)
		{
			m2 = (p + i)->weight;
			index2 = i;
		}
	}

	*s11 = index1;
	*s22 = index2;
}

/*
HT为二级指针,指针元素所指向的是为HNode结构体的指针数组,weight为权重数组; n为叶子节点的个数
该函数的目的就是建造一个哈夫曼树
*/
void CreatHuffTree(HuffmanTree* HT, int* weight, int n)
{
	int NodeNumber = 2 * n - 1;
	*HT = new HNode[NodeNumber + 1];//创建指针数组
	HuffmanTree p = *HT;//创建遍历结构体指针
	(p + 0)->weight = 0;

	for (int i = 1; i <= n; i++)
	{
		(p + i)->weight = *(weight + i - 1);
		(p + i)->parent = 0;
		(p + i)->left = 0;
		(p + i)->right = 0;
	}
	for (int i = n + 1; i <= NodeNumber; i++)
	{
		(p + i)->weight = 0;
		(p + i)->parent = 0;
		(p + i)->left = 0;
		(p + i)->right = 0;
	}
	for (int i = n + 1; i <= NodeNumber; i++)//树里有n-1个度为2的节点,所以可以循环n-1次
	{
		int s1, s2;
		SelectMin(*HT, &s1, &s2,i - 1);//为啥要i-1,因为i-1之后权重全部为0。
		(p + i)->weight = (p + s1)->weight + (p + s2)->weight;
		(p + i)->left = s1;
		(p + i)->right = s2;
		(p + s1)->parent = i;
		(p + s2)->parent = i;
	}
	p = NULL;
}

int main()
{
	HuffmanTree T;
	HuffmanCode C;
	int n;
	cin >> n;//读取叶子结点个数
	int* w = new int[n];
	for (int i = 0; i < n; i++) { cin >> *(w + i); }//读取权值
	CreatHuffTree(&T, w, n); 
	//CreatHauffmanCoding(&C, T, n);
	for (int i = 1; i <= 2 * n - 1; i++)
	{
		cout << T[i].weight << "||" << T[i].parent << " " << T[i].left << " " << T[i].right << endl;
	}
	int aa;
	aa = 10;
	return 0;
}

结果验证:

哈夫曼编码实现:

#include<iostream>
#include<string>

using std::cin;
using std::cout;
using std::endl;

typedef char** HuffmanCode;

typedef class 
{
public:

	unsigned int weight;//节点权重
	unsigned int parent, left, right;//双亲节点,左节点,右节点在指针数组里的位置
}HNode, *HuffmanTree;

/*
p_HT为结构体指针,s1,s2是没有父节点的节点中最小权值的两个节点的位置,且s1小于s2,end为存放节点的前一个位置
该函数的目的是在还未有父节点的节点中,找到两个权值最小的节点,并确定它们的位置
*/
void SelectMin(HuffmanTree p_HT, int* s11, int* s22, int end)
{
	int m1, m2, index1, index2;
	HuffmanTree p = p_HT;
	m1 = 10000; m2 = 10000;
	index1 = 0; index2 = 0;


	for (int i = 1; i <= end; i++)
	{
		if ((p + i)->parent != 0) { continue; }
		else if ((p + i)->weight < m1)
		{
			m2 = m1;//假设m1小于m2
			m1 = (p + i)->weight;
			index2 = index1;
			index1 = i;
		}
		else if ((p + i)->weight <= m2)
		{
			m2 = (p + i)->weight;
			index2 = i;
		}
	}

	*s11 = index1;
	*s22 = index2;
}

/*
HT为二级指针,指针元素所指向的是为HNode结构体的指针数组,weight为权重数组; n为叶子节点的个数
该函数的目的就是建造一个哈夫曼树
*/
void CreatHuffTree(HuffmanTree* HT, int* weight, int n)
{
	int NodeNumber = 2 * n - 1;
	*HT = new HNode[NodeNumber + 1];//创建指针数组
	HuffmanTree p = *HT;//创建遍历结构体指针
	(p + 0)->weight = 0;

	for (int i = 1; i <= n; i++)
	{
		(p + i)->weight = *(weight + i - 1);
		(p + i)->parent = 0;
		(p + i)->left = 0;
		(p + i)->right = 0;
	}
	for (int i = n + 1; i <= NodeNumber; i++)
	{
		(p + i)->weight = 0;
		(p + i)->parent = 0;
		(p + i)->left = 0;
		(p + i)->right = 0;
	}
	for (int i = n + 1; i <= NodeNumber; i++)//树里有n-1个度为2的节点,所以可以循环n-1次
	{
		int s1, s2;
		SelectMin(*HT, &s1, &s2,i - 1);//为啥要i-1,因为i-1之后权重全部为0。
		(p + i)->weight = (p + s1)->weight + (p + s2)->weight;
		(p + i)->left = s1;
		(p + i)->right = s2;
		(p + s1)->parent = i;
		(p + s2)->parent = i;
	}
	p = NULL;
}

/*
HC为指向二维字符数组的指针,p_HT为数组指针,是存储哈夫曼树的数组的第一个元素,n是叶子节点数
该函数的目的是根据哈夫曼树生成哈夫曼编码
*/
void CreatHauffmanCoding(HuffmanCode* HC, HuffmanTree p_HT, int n)
{
	int m = 2 * n - 1;
	int start, c, j;
	*HC = new char* [n + 1];//因为哈夫曼树的结构体数组0单元不存储,所以为了相对应,则分配n+1
	char* cd = new char[n];

	for (int i = 1; i <= n; i++)
	{
		start = n - 1;
		cd[start] = '\0';
		c = i;//c就是该节点在顺序表里的位置
		j = (p_HT + i)->parent;//j代表的是该节点双亲节点在顺序表里的位置

		while (j)
		{
			--start;
			if ((p_HT + j)->left == c)
			{
				cd[start] = '0';
			}
			else { cd[start] = '1'; }
			c = j;
			j = (p_HT + c)->parent;
		}

		(*HC)[i] = new char[n - start];
		strcpy_s(*(*(HC) + i), n - start, &cd[start]);
	}

	delete[] cd;
	cd = NULL;
}

int main()
{
	HuffmanTree T;
	HuffmanCode C;
	int n;
	cin >> n;//读取叶子结点个数
	int* w = new int[n];
	for (int i = 0; i < n; i++) { cin >> *(w + i); }//读取权值
	CreatHuffTree(&T, w, n); 
	CreatHauffmanCoding(&C, T, n);
	for (int i = 1; i <= n; i++)
	{
		cout << T[i].weight << "||" << *(C + i) << endl;;
	}
	int aa;
	aa = 10;
	return 0;
}

验证:

参考文章: 

1:6.5 哈夫曼树及其应用 - 知乎 

2:哈夫曼编码及其应用 - 掘金 

3:哈夫曼树(最优二叉树)详解

调试过程中的问题

逻辑错误_不放回的情况下每次一定要找到数组里最小的两个元素_2301_77540499的博客-CSDN博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值