赫夫曼树
定义:
假设有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;
}
}