赫夫曼树笔记

赫夫曼树的基本概念

在认识赫夫曼树之前,我们需要弄清以下的相关概念、
1、路径

路径就是从某一个结点往下到达另一个结点所走的通路

2、路径长度

路径长度就是某一路径所经过的边的数量(两个结点之间的连线)

3、结点的带权路径长度

若给每一个结点分配一个权重,则该结点的带权路径长度等于
(路径长度)* 权重, 此时路径长度指从根结点走到该结点

4、树的带权路径长度

树的带权路径长度为所有叶子结点的带权路径长度之和

5、哈夫曼树

给定n个权值赋给n个叶子结点,构建一个二叉树,使得树的带权路径长度最小的树称为哈夫曼树,也成为最优二叉树

例如A的带权路径长度便为:3 * 5 = 15
例如从
所以由上我们可以知道,不同二叉树的带权路径长度与二叉树的构型(即叶子结点的分布)有关

根据树的带权路径长度的计算规则,我们应该尽可能地让权值大的叶子结点靠近根结点,让权值小的叶子结点远离根结点,这样便能使得这棵二叉树的带权路径长度达到最小。

哈夫曼树的构建

构建思路:

1、初始状态下有n个结点,每个结点有一个权值。
2、在所有结点中找到权值最小的两棵树,生成他们的父结点,权值为这两个结点的权值之和,相当于看成一颗树
3、重复操作2,直到最后只剩下一棵树,即为哈夫曼树
在这里插入图片描述

代码实现:

数组下标为0的地方不储存数据!!

typedef double ElemType;

typedef struct HTNode{ //结点数据类型
	ElemType weight; //权值
	int parent; //父结点
	int lc, rc; //左孩子和右孩子
} *HuffTree;

//在下标1到i-1中找到权重值最小的两个下标,以引用传递的方式返回,
//其中s1的权值小于s2的权值
void Select(HuffTree &HT, int n, int &s1, int &s2)
{
	int min;
	for(int i=1; i<=n; i++)
	{
		if(HT[i].parent == 0)
			min = i;
			break;
	}
	//找第一个最小值
	for(int i=min+1; i<=n; i++)
	{
		if(HT[i].parent == 0 && HT[i].weight < HT[min].weight)
		{
			min = i;
		}
	}
	s1 = min;
	找第二个最小值
	for(int i=1; i<=n; i++)
	{
		if(HT[i].parent == 0)
			min = i;
			break;
	}
	//找第一个最小值
	for(int i=min+1; i<=n; i++)
	{
		if(HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1)
		{
			min = i;
		}
	}
	s2 = i;
}

//传入n个数据来创建创建哈夫曼树, 初始状态,所有的
void CreateHuff(HuffTree &HT, ElemType *w, int n)
{
	int m = 2*n - 1; //哈夫曼树的结点总数;
	HT = (HuffTree)malloc(sizeof(HTNode) * (m+1));
	//申请m+1个因为数组零下标不存放数据
	for(int i=1; i<=n; i++)
		HT[i].weight = w[i-1];
	//构建哈夫曼树
	for(int i=n+1; i<=m; i++)
	{
	//选择权值最小的w1和w2,生成他们的父结点
		int w1, w2;
		Select(HT, i-1, w1, w2);
		HT[i].weight = HT[w1].weight + HT[w2].weight;
		HT[w1].parent = i;
		HT[w2].parent = i;
		HT[i].lc = w1;
		HT[i].rc = w2;
	}
	//打印测试代码;
	printf("哈夫曼树为:>\n");
	printf("下标   权值     父结点   左孩子   右孩子\n");
	printf("0                                  \n");
	for (int i = 1; i <= m; i++)
	{
		printf("%-4d   %-6.2lf   %-6d   %-6d   %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lc, HT[i].rc);
	}
	printf("\n");
}

哈夫曼编码

编码生成思路

对于任意一颗二叉树来说,把左分支记为0,右分支记为1,那么从根结点到该结点的路径自然就组成了一串数字
在这里插入图片描述
具体形成的编码表如下图:
在这里插入图片描述

具体代码:

typedef char ** HuffTreeCode;

void HuffmanCode(HuffTree &HT, HuffTreeCode &code,int n){
	HC = (HuffTreeCode) malloc(sizeof(char *)*(n + 1));//下标为0的空间不用,所以开n+1个空间
													 //HuffTreeCode为char **类型,即字符指针数组
	char *code = (char *)malloc(sizeof(char)*n);
	code[n-1] = '\0';//辅助存储空间最后一个位置为0
	for(int i=1; i<=n; i++)
	{
		int start = n-1;//每次将start指向'\0'的位置
		int temp = i;//正在进行第i个数据编码
		int p = HT[i].parent;
		while(p) //根结点的parent为0
		{
			if(HT.lc == i)
				code[--start] = 1;
			else if(HT.rc == i)
				code[--start] = 0;
			temp = p;
			p = HT[temp].parent;
		}
		HC[i] = (char)malloc(sizeof(char)*(n+1));//开n+1个空间是因为斜树最长情况下字符数组中为n个1,还有字符串结尾标志
		strcpy(HC[i], code);
	}
	free(code);
}

本文转载借鉴:https://blog.csdn.net/chenlong_cxy/article/details/117929139?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164534775416781683919602%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164534775416781683919602&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-2-117929139.pc_search_result_positive&utm_term=%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91&spm=1018.2226.3001.4187

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值