c++数据结构:树(概念三)赫夫曼树

赫夫曼树:也称为最优二叉树,是一类带权路径长度最短的树。

  • 路径:树中的一个节点和另一个节点之间的分支构成这两个节点之间的路径
  • 路径长度:路径上的分支数目
  • 树的路径长度:从树根到每一个节点的路径长度之和,完全二叉树是路径长度最短的二叉树。
  • 带权节点:节点到树根之间的路径长度与权的乘积
  • 树的带权路径长度:树中所有叶子节点的带权路径长度之和 记作:WPL
  • 赫夫曼树的特点:权越大的叶子离根越近

其中在n个节点中WPL最小的二叉树称为最优二叉树或赫夫曼树。

 赫夫曼算法(构建赫夫曼树)

  1. 根据给定的n个圈值{W1,W2....Wn}构成n棵二叉树的集合F={T1,T2,...,Tn},其中每颗二叉树Ti中只有一个带权为Wi的根节点,其左右子树均空。
  2. 在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为左、右子树根节点的权值之和。
  3. 在F中删除这两棵树,同时将新的二叉树加入到F中。
  4. 重复(2)(3),直到F只剩一颗树为止。

 

  • 赫夫曼树只有度为2和0的节点
  • n棵二叉树需要合并n-1次

赫夫曼树的存储结构:

 顺序结构存储:一维结构数组

class HTnode
{
public:
	int weight;//权值
	int parent;//双亲
	int lchild;//左孩子
	int rchild;//右孩子
};
class HuffmanTree
{
public:
	HTnode* huffmantree;//动态分配数组存储赫夫曼树
};

一颗n个节点的赫夫曼树共有2n-1个节点,所以需要分配2n-1个空间

构建赫夫曼树的过程:

 代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

//赫夫曼树
class HTnode
{
public:
	int weight;//权值
	int parent;//双亲
	int lchild;//左孩子
	int rchild;//右孩子
}*huffmantree;//动态分配数组存储赫夫曼树
class HuffmanTree
{
public:
	HTnode* huffmantree;//动态分配数组存储赫夫曼树
};

void select(HuffmanTree* p, int a, int& b, int& c)//寻找在HT[1-n-1]中parent为0,且weight最小的两个节点
{
	vector<int> p1;//创建一个容器,存放
	for (int i = 1; i < a; i++)
	{
		if (p->huffmantree[i].parent == 0)
		{
			p1.push_back(i);//如果parent不为0,存放到数组中
		}
	}
	sort(p1.begin(), p1.end());//从小到大排序
	b = p1[1];
	c = p1[2];
}
void HuffmanCoding(HuffmanTree* HT, int* w, int n)//HT为赫夫曼树,W为各节点的权值,n为节点数
{
	if (n <= 1)return;
	int m = n * 2 - 1;//赫夫曼树共有n*2-1个节点
	HT->huffmantree = new HTnode[m + 1];//分配内存,0号位置不使用
	HuffmanTree* p = HT;
	for (int i = 1; i <= n; ++i, ++p->huffmantree, ++w)
	{
		*p->huffmantree = { *w,0,0,0 };//分配数组
	}
	for (int i = n + 1; i <= m; i++, p->huffmantree++)
	{
		*p->huffmantree = { 0,0,0,0 };//初始化其他空间
	}
	//开始构建赫夫曼树
	for (int i = n + 1; i <= m; i++)
	{
		int x = 0;//用来存储最小元素,且parent为0的数组位置
		int y = 0;//用来存储次小元素,且parent为0的数组位置
		select(HT, i - 1, x, y);
		HT->huffmantree[x].parent = i; HT->huffmantree[y].parent = i;//设置双亲
		HT->huffmantree[i].lchild = x; HT->huffmantree[i].rchild = y;//设置新节点的左右孩子
		HT->huffmantree[i].weight = HT->huffmantree[x].weight + HT->huffmantree[y].weight;//设置权值
	}
}

赫夫曼编码:

在通信中,会把要传输的内容转化为二进制的形式表达。

设计的要求:

  • 出现较多的字符使用尽可能短的编码
  • 总长度尽可能的短

所以设计时,必须是任意一个字符的编码都不是另一个字符编码的前缀,这种码也叫前缀编码。

该方法完全依据字符出现概率来构造异字头的平均长度最短的码字。

通过赫夫曼树来进行设计:

  • 左孩子单个路径为0
  • 右孩子单个路径为1
  • 从根开始探索

  •  001111010110  等价于:ADCCB
  • BBCDE 等价于:1101101011101

赫夫曼编码为前缀编码的原因:

因为没有一片树叶是另一个树叶的祖先,所以每一个叶节点的编码就不可能是其他叶子节点编码的前缀。

求叶子的赫夫曼编码 :

求赫夫曼编码使用逆序的方式进行获取:叶子到根

代码的实现:

void HeffmanCoding(HuffmanTree& HT, char** HC, int n)//赫夫曼树,赫夫曼编码表,叶子个数
{
	HC = new char* [n + 1];//分配n个字符编码的头指针向量
    char* cd = new char[n];//存放编码所需的空间
    cd[n - 1] = '\0';//结束符
	for (int i = 1; i <= n; i++)//从1-n的节点赫夫曼编码
	{
		int start = n - 1;//从后往前存储,记录最后的位置
		for (int c = i,int f = HT.huffmantree[i].parent; f != 0; c = f, f = HT.huffmantree[f].parent)
		{
			if (HT.huffmantree[f].lchild == c) cd[--start] = '0';//判断左右子树
			else
			{
				cd[--start] = '1';
			}
			HC[i] = new char [n - start];//为第i个字符编码分配空间
			strcpy(HC[i], &cd[start]);//拷贝内容;
		}
	}
    delete cd;//释放内存
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值