哈夫曼树:
n个带权值节点构造的二叉树中带权路径长度最短的二叉树,并且n个节点都是叶子节点,只有叶子节点才是有效的节点,又称为最优二叉树;
带权路径长度(WPL):
树中所有叶子节点到根节点的路径长度和该叶子节点权值乘积之和。
WPL=W1*L1+W2*L2+…+Wi*Li;
Wi:表示第i个叶子节点的权值;
Li:表示第i个叶子节点到根节点的路径长度。
构造哈夫曼树:
1.将给定的n个权值,每个权值可以看作是一颗只有根节点的树,组成一个集合;
2.在集合中选出根节点权值最小的两棵树,将其权值相加得到新的权值,构造新的树;
3.将刚被选中的两颗树从集合中删去,并将新权值构造的树加入集合中;
4.重复上述两步,直至集合中只剩一棵树。
代码实现哈夫曼树:
#include"Heap.h"
using namespace std;
//哈夫曼树节点类
template<class T>
struct HuffmanTreeNode
{
//构造函数
HuffmanTreeNode(const T& x)
:_w(x)
, _left(NULL)
, _right(NULL)
{}
T _w;
HuffmanTreeNode<T> *_left;
HuffmanTreeNode<T> *_right;
};
//构建哈夫曼树
template<class T>
class HuffmanTree
{
typedef HuffmanTreeNode<T> Node;
public:
HuffmanTree()
:_root(NULL)
{}
HuffmanTree(T* arr, size_t size, const T& invalid)
{
//内部类(重载Heap仿函数)
struct Compare
{
bool operator()(Node* l, Node* r)const
{
return l->_w < r->_w;
}
};
//建小堆每次选出数组中两个最小的数进行构造哈夫曼树
//Heap里面传节点为了能将以前和现在的节点连接在一起
Heap<Node*, Compare > Min_Heap;
for (int i = 0; i < (int)size; ++i)
{
if (arr[i] != invalid)
{
Min_Heap.Push(new Node(arr[i]));
}
}
while (Min_Heap.Size()>1)
{
Node* left = Min_Heap.Top();
Min_Heap.Pop();//每一次执行Pop操作之后,堆就被重新再调整一次,再选出次大的数
Node* right = Min_Heap.Top();
Min_Heap.Pop();
//将最小的两个数相加的结果存放在一个parent节点中
Node* parent = new Node(left->_w + right->_w);
//将parent节点和left,right节点链起来,再入堆选出最小的两个数
parent->_left = left;
parent->_right = right;
Min_Heap.Push(parent);
}
_root = Min_Heap.Top();
}
Node* Get_Top()
{
return _root;
}
//析构函数
~HuffmanTree()
{
if (_root)
{
Destroy(_root);
}
}
void Destroy(Node* root)
{
if (root == NULL)
{
return;
}
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
private:
Node* _root;
};
void Test()
{
int a[] = { 1, 5, 2, 4, 6, 7, 0, 3, 8, 9 };
HuffmanTree<int> h(a, sizeof(a) / sizeof(a[0]), 0);
}
哈夫曼树的特性:
1.哈夫曼树没有度为1的节点;
2.权值越小离根节点越远,权值越大离根节点越近;
3.哈夫曼树左右子树交换,不会影响带权路径长度;
4.叶子节点才是有效的节点,非叶子节点都是用叶子节点构造出来的;
5.一颗有n个叶子节点的哈夫曼树,总节点为2*n-1。
哈夫曼树的应用:
给定一段字符串,如何对字符串进行编码,使得该字符串编码存储空间最小?
——哈夫曼编码。