浅析huffman树原理及实现
哈夫曼树(Huffman Tree),又称最优二叉树,是一类带权路径长度最短的树。假设有n个权值{w1,w2,...,wn},如果构造一棵有n
个
叶子节点的二叉树,而这n个叶子节点的权值是{w1,w2,...,wn},则所构造出的带权路径长度最小的二叉树就被称为哈夫曼树
带权路径长度: 树的带权路径长度指树中所有叶子节点到根节点的路径长度与该叶子节点权值的乘积之和,如
果在一棵二叉树中共
有n
个叶子节点,用Wi表示第i个叶子节点的权值,Li表示第i个也叶子节点到根节点的路径长度,则该二叉树的带
权路径长
度:
WPL=W1*L1 + W2*L2 + ... Wn*Ln.
哈夫曼树的特性:
1.权值越大的离根节点越远,权值越小的离根节点越近.(贪心算法思想)
2.huffman树的左右子树可以进行交换. 不会影响到带权路径长度.
3.带权值的节点都是叶子结点, 非叶子结点的权值都是左子树右子树的权值总和.
4.一颗只有n个叶子结点的huffman树一共会有2n-1个节点.
我们来理解为什么拥有这些特性会有什么用?? 突然最近你们期末考试了,老师最后会将成绩推送到教务网之上,然后教务网对
你的成绩做出A,B,C,D,E,F的划分,然后还有成绩范围划分标准以及每个成绩范围占总数的百分比. 这个时候学校需要让你来完成
这个系统,然后你可以怎么做? 你完全可以写一个这样的逻辑.
这种逻辑在我们每个成绩区间所占比的人数一样的时候最实用,但是成绩区间占比不同的时候,我们再尝试huffman算法构建一个逻
辑试一试:
我们使用huffman树的判定方式很明显比第一种高,并且我可以说这是效率最高的一种判定方式. 这就是huffman的魅力..
实现huffman树
如何构建huffman树:
1、将给定的n个权值看做n棵只有根节点(无左右孩子)的二叉树,组成一个集合HT,每棵树的权值为该节点的权值。
2、从集合HT中选出2棵权值最小的二叉树,组成一棵新的二叉树,其权值为这2棵二叉树的权值之和。
3、将步骤2中选出的2棵二叉树从集合HT中删去,同时将步骤2中新得到的二叉树加入到集合HT中。
4、重复步骤2和步骤3,直到集合HT中只含一棵树,这棵树便是赫夫曼树。
*****************************************************************heap.h********************************************************
template<class T>
struct Less
{
bool operator()(const T& l, const T& r)const
{
return l < r;
}
};
template<class T>
struct Greator
{
bool operator()(const T& l, const T& r)const
{
return l > r;
}
};
template<class T, class Compare>
class Heap
{
public:
Heap()
{}
Heap(T* a, size_t n)
{
_a.reserve(n);
for (size_t i = 0; i < n; ++i)
{
_a.push_back(a[i]);
}
//建堆
for (int i = (_a.size() - 2) / 2; i >= 0; --i)
{
_AdjustDown(i);
}
}
void Push(const T& x)
{
_a.push_back(x);
_AdjustUp(_a.size() - 1);
}
void Pop()
{
assert(!_a.empty());
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
_AdjustDown(0);
}
const T& Top() const
{
return _a[0];
}
size_t _size()
{
return _a.size();
}
protected:
void _AdjustDown(size_t root)
{
Compare ptr;
int parent = root;
int child = parent * 2 + 1;
while (child < _a.size())
{
if (child + 1 < _a.size() && ptr(_a[child + 1], _a[child]))
{
++child;
}
if (ptr(_a[child], _a[parent]))
{
swap(_a[parent], _a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void _AdjustUp(int child)
{
Compare fun;
int parent = (child - 1) >> 1;
while (child > 0)
{
if (fun(_a[child], _a[parent]))
{
swap(_a[child], _a[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{
break;
}
}
}
protected:
vector<T> _a;
};
#include<iostream>
#include<windows.h>
#include"heap.h"
using namespace std;
template<class W>
struct HuffmanTreeNode
{
HuffmanTreeNode<W>* _left;
HuffmanTreeNode<W>* _right;
HuffmanTreeNode<W>* _parent;
W _w; // 权值
HuffmanTreeNode(const W& w)
:_w(w)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
template<class W>
class HuffmanTree
{
typedef HuffmanTreeNode<W> Node;
public:
HuffmanTree()
:_root(NULL)
{}
HuffmanTree(W* a, size_t n, const W& invalid)
{
// 内部类
struct NodeCompareX
{
bool operator()(const Node* left, const Node* right) const
{
return left->_w < right->_w;
}
};
Heap<Node*, NodeCompareX> minHeap;
//创建哈夫曼树
for (size_t i = 0; i < n; ++i)
{
if (a[i] != invalid)
{
minHeap.Push(new Node(a[i]));
}
}
while (minHeap._size() > 1)
{
Node* left = minHeap.Top();
minHeap.Pop();
Node* right = minHeap.Top();
minHeap.Pop();
Node* parent = new Node(left->_w + right->_w);
parent->_left = left;
parent->_right = right;
left->_parent = parent;
right->_parent = parent;
minHeap.Push(parent);
}
_root = minHeap.Top();
}
Node* GetRoot()
{
return _root;
}
size_t size(Node* root)
{
if (root == NULL)
{
return 0;
}
size_t i = size(root->_left);
size_t j = size(root->_right);
return i + j + 1;
}
protected:
Node* _root;
};
huffman树的而应用
huffman编码:
赫夫曼树的应用十分广泛,比如众所周知的在通信电文中的应用。在等传送电文时,我们希望电文的总长尽可能短,因此可以对每
个字符设计长度不等的编码,让电文中出现较多的字符采用尽可能短的编码。为了保证在译码时不出现歧义,我们可以采取如下图
所示的编码方式: