在这里关于什么是哈夫曼树就不多说了,自己去查阅相关的资料书籍,下面来说说如何实现哈夫曼树。
首先语言描述一下实现的步骤:假设这里给出得到数组元素为5,3,1,7
1.在当前数组中找出最小的两个数1,3各自为一个结点生成如下:
此时将4放回原数组,则数组元素变为5 4 7,然后在当前数组中找出最小的两个数4,5生成如下
此时将9放回原数组,则数组变为9 7,继续找出最小的两个树,7,9组成如下
此时将16放回数组,数组中只剩下一个数了,此时构成的这棵树就是哈夫曼树。
下面我来说说代码的实现。
首先实现哈夫曼树要借助堆的功能。先给出代码然后在解释
因为这里用的我自己写的堆的代码,所以先给出堆的代码:
#include<iostream>
#include<vector>
using namespace std;
template<class T>
class Less
{
public:
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& left, const T& right)
{
return left >right;
}
};
//这里创建的都是小堆
template<class T, class Compare = Less<T>>
class Heap//堆
{
public:
Heap(){}
Heap(T* array, size_t size)
:_size(size)
{
_array.resize(size);
for (size_t i = 0; i < _size; i++)
{
_array[i] = array[i];
}
_AdjustDown((_size - 2) >> 1);
}
/*void _downtoup()
{
downtoup((_size - 2)>>1);
}*/
T Top() const//返回堆顶的元素
{
return _array[0];
}
void Push(const T& data)//给堆中插入元素,使用向上调整插入
{
//方法1,直接尾插
_array.push_back(data);
_size++;
if (_array.size() > 1)
{
_AdjustUp(_size - 1);
}
}
bool Empty()
{
if (_size != 0)
return true;
else
return false;
}
int size() const
{
return _size;
}
void Pop()//删除堆中的元素其实,就是删除堆顶的元素
{
if (_array.empty())
return;
size_t last = _size - 1;
swap(_array[0], _array[last]);
_array.pop_back();
_size--;
if (_size > 1)
{
_AdjustDown(0);
}
}
private:
void _AdjustDown(int parent)//从上往下调整,建立小堆
{
for (int i = parent; i >= 0; i--)
{
size_t child = parent * 2 + 1;
parent = i;
while (child<_size)
{
Compare com;
if (child + 1 < _size&&com(_array[child + 1], _array[child]))
child += 1;
if (child<_size&&com(_array[child], _array[parent]))
{
swap(_array[parent], _array[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
}
void _AdjustUp(size_t child)//向上调整,插入中使用
{
size_t parent = (child - 1) >> 1;
while (child>0)
{
if (Compare()(_array[child], _array[parent]))
{
swap(_array[parent], _array[child]);
child = parent;
parent = (child - 1) >> 1;
}
else
break;
}
}
private:
vector<T> _array;
size_t _size;//缺点就是每次插入的需要调整
};
下面给出哈夫曼的实现代码
template<class W>
struct HuffmanTreeNode
{
public:
HuffmanTreeNode<W>* _pLeft;
HuffmanTreeNode<W>* _pRight;
W _weight;
public:
HuffmanTreeNode(const W& weight)
:_pLeft(NULL)
, _pRight(NULL)
, _weight(weight){}
};
template<class W>
class HuffmanTree
{
public:
typedef HuffmanTreeNode<W>* PNode;
public:
HuffmanTree()
:_pRoot(NULL){}
HuffmanTree(W *array, size_t size,const W& invalid )
{
_CreatHuffmanTree(array, size,invalid);
}
~HuffmanTree()
{
_Destory(_pRoot);
}
private:
void _Destory(PNode& pRoot)
{
if (pRoot)
{
_Destory(pRoot->_pLeft);
_Destory(pRoot->_pRight);
delete pRoot;
}
}
void _CreatHuffmanTree(W* array, size_t size,const W& invalid)//创建二叉树森林
{
struct Compare
{
bool operator()(PNode pLeft, PNode Pright)
{
return pLeft->_weight < Pright->_weight;
}
};
Heap<PNode,Compare> hp;//堆里面存的是结点的地址
for (size_t i = 0; i < size; i++)//插入堆的时候自动排序
{
if (array[i] != invalid)
{
hp.Push(new HuffmanTreeNode<W>(array[i]));//将结点的地址加入到堆中
}
}
if (hp.Empty())
_pRoot = NULL;
while (hp.size()>1)
{
PNode pLeft = hp.Top();
hp.Pop();//pop出去会自动调整的不都需担心
PNode pRight = hp.Top();
hp.Pop();
PNode parent = new HuffmanTreeNode<W>(pLeft->_weight + pRight->_weight);
parent->_pLeft = pLeft;
parent->_pRight = pRight;
hp.Push(parent);
}
_pRoot = hp.Top();
}
private:
PNode _pRoot;
};
int main()
{
int arr[] = {5,3,1,7};
HuffmanTree<int> s(arr, 4, 0);
system("pause");
return 0;
}
关于堆的代码我就不多说了,上一篇文章是专门讲堆的,不懂的点击如下链接查看
下面我重点介绍哈夫曼树的实现过程
1.首先当然后树的结点了
template<class W>
struct HuffmanTreeNode
{
public:
HuffmanTreeNode<W>* _pLeft;
HuffmanTreeNode<W>* _pRight;
W _weight;
public:
HuffmanTreeNode(const W& weight)
:_pLeft(NULL)
, _pRight(NULL)
, _weight(weight){}
};
结点里面的内容包括左右孩子的指针和权值_weight,这个没什么好说的
2.下面看如何实现哈夫曼树
上面的两幅图将如何借助堆创建哈夫曼树的过程已经很详细的介绍了,想必大家一定很清楚了!不懂的私我。
下面给出测试结果