赫夫曼树及求解最小WPL的实现

赫夫曼树

定义:

假设有m个权值{W1,W2,...Wm},可以构造一颗含有n个叶子节点的二叉树,每个叶子节点的权为Wj,则其中树的带权路径长度即WPL最小的二叉树称作最优二叉树或赫夫曼树。

算法思想:

由于赫夫曼树的构造决定了树中没有度为1的节点,则一颗有n个叶子节点的赫夫曼树共有2n-1个节点。则可以将其存储在容量为2n-1的数组中。
节点存储可以表示为

template<typename T>
struct HTnode
{
	T weitht;
	int parent, lchild, rchild;
};

其中parent、lchild、rchild之所以是int型是因为存放的是数组的下标。
赫夫曼树的实现可分为两大部分(叶子节点为n):
(1)初始化。将所有单元中的双亲、左孩子、右孩子初始化为0,再将n个叶子节点的权值初始化。
(2)通过n-1次的选择、删除、合并、来创建赫夫曼树:选择是从当前森林中选择双亲为0且权值最小的两个树根节点s1,s2.删除是指将节点s1,s2的双亲改为非0,合并就是讲s1,s2的权值加起来作为一个新节点的权值依次存入到数组的第n个之后的单元中。

求解最优WPL:

构造完赫夫曼树之后,此时赫夫曼树的WPL即是最小的。WPL等于树中所有叶子节点的带权路径长度之和。因此,需要先求出一个叶子节点的带权路径长度,也就是从该节点到树根节点之间的路径长度与节点上权的乘积。需要明白,此时数组中节点的parent为0的即是根节点。

C++代码实现:

#pragma once
#include <vector>
using namespace std;

template<typename T>
struct HTnode
{
	T weitht;
	int parent, lchild, rchild;
};
/**
 * 赫夫曼树的C++实现
 * @author  wzx
 * @data    2019年3月16日
 */
template <typename type>
class HuffmanTree
{
public:
	HuffmanTree();
	HuffmanTree(int[], int);
	~HuffmanTree()=default;
	//提供获取赫夫曼树的带权路径长度接口
	int getWPL();	
private:
	int m_HuffmanTreeLength; ; //指示赫夫曼树中的叶子节点的数量
	vector <HTnode<type>> hTnodes;
	//在vector下标从0到k中找到双亲节点为0,且权值最小的两个节点
	void select(int k, int &s1, int &s2); 
	void createHuffmanTree();
};
template <typename type>
HuffmanTree<type>::HuffmanTree():m_HuffmanTreeLength(0)
{}
template <typename type>
HuffmanTree<type>::HuffmanTree(int weights[],int nLength)
{
	for (int i{}; i < nLength; ++i)
	{
		HTnode<type> hTnode = { weights[i],0,0,0 };
		hTnodes.push_back(hTnode);
	}
	for (int i{ nLength }; i < nLength * 2 - 1; ++i)
	{
		HTnode<type> hTnode = {0,0,0,0};
		hTnodes.push_back(hTnode);
	}
	m_HuffmanTreeLength = nLength;
	createHuffmanTree();
}
template <typename type>
void HuffmanTree<type>::select(int k, int &s1, int &s2)
{
	int firstMinWeight = hTnodes.at(0).weitht;
	s1= 0;
	//找到第一个权值最小的节点
	for (int i=0; i <= k; ++i)
	{
		if ((firstMinWeight > hTnodes.at(i).weitht)&&(hTnodes.at(i).parent == 0))
		{
			firstMinWeight = hTnodes.at(i).weitht;
			s1 = i;
		}
	}
	//找到第二个权值最小的节点
	//快速排序,找到符合条件的secMinWeight的初始值
	int	secMinWeight = 0;
	int j = 0;
	while (hTnodes.at(j).parent != 0||j==s1)
	{
		++j;
	}
	secMinWeight = hTnodes.at(j).weitht;
	s2 = j;
	for (int i = 0; i <=k; ++i)
	{
		if(i==s1)
			continue;
		else
		{
			if ((secMinWeight > hTnodes.at(i).weitht) && (hTnodes.at(i).parent == 0))
			{
				secMinWeight = hTnodes.at(i).weitht;
				s2 = i;
			}
		}
	}
}

template <typename type>
void HuffmanTree<type>::createHuffmanTree()
{
	int s1 = 0, s2 = 0;
	for (int i = m_HuffmanTreeLength; i < 2 * m_HuffmanTreeLength - 1; ++i)
	{
		select(i - 1, s1, s2);
		hTnodes[s1].parent = i;
		hTnodes[s2].parent = i;
		hTnodes[i].lchild = s1;
		hTnodes[i].rchild = s2;
		hTnodes[i].weitht = hTnodes[s1].weitht + hTnodes[s2].weitht;
	}
}

template <typename type>
int HuffmanTree<type>::getWPL()
{
	int total=0;
	if (m_HuffmanTreeLength <= 1)
		return -1;
	else
	{
		for (int i{}; i < m_HuffmanTreeLength; ++i)
		{
			int count = 0;
			int f = hTnodes[i].parent;
			while (f != 0)
			{
				++count;
				f = hTnodes[f].parent;
			}
			total += count * hTnodes[i].weitht;
		}
		return total;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值