这里演示建树过程:
左小,右大。
思路:
找到最小和次小,合并形成一个新的父节点,(所以一个结点有三个指针域)
从叶子往上建树的过程。
所以关键是:1.找到最小和次小,
2. 合并
下面展示过程代码:
template <typename T, typename E>//一个结点自带三个指针
struct HuffmanNode
{
E data;
HuffmanNode<T, E>* leftChild, * rightChild, * parent;
HuffmanNode() :leftChild(NULL), rightChild(NULL), parent(NULL) {}//构造函数
HuffmanNode(E elem, HuffmanNode<T, E>* pr, HuffmanNode<T, E>* left, HuffmanNode<T, E>* right)
: data(elem),parent(pr), leftChild(left), rightChild(right) {}
//重载操作符:
bool operator > (HuffmanNode<T, E> right) {
return data > right.data;
}
bool operator >= (HuffmanNode<T, E> right) {
return (data > right.data) || (data == right.data);
}
bool operator < (HuffmanNode<T, E> right) {
return data < right.data;
}
bool operator <= (HuffmanNode<T, E> right) {
return (data < right.data) || (data == right.data);
}
bool operator == (HuffmanNode<T, E> right) {
return data == right.data;
}
};
下面生成哈夫曼树
//Huffman树类定义
template <typename T, typename E>
class HuffmanTree {
public:
HuffmanTree(E w[], int n);
~HuffmanTree() { deleteTree(root);}
HuffmanNode<T, E>* getRoot() {return root;}
void output(HuffmanNode<T, E>* t, string str, ostream& out);
void preorder(HuffmanNode<T, E>* t);
protected:
HuffmanNode<T, E>* root;
void deleteTree(HuffmanNode<T, E>* t);
void mergeTree(HuffmanNode<T, E> *bt1, HuffmanNode<T, E> *bt2,
HuffmanNode<T, E>*& parent);
};
用数组初始化该树,就不用一个一个输入数字。
第一种建树方式
1.把哈夫曼结点保存在堆中
2.用first 和second new两个结点,重新保存堆中的结点值,把左右孩子的父节点连上自己。
3.合并first,second, 父节点的左右指针连接,数据重新计算。
4.再插入堆中
5.最后把父节点从堆中拿出,只要值、
void HuffmanTree::mergeTree(HuffmanNode *ht1, HuffmanNode *ht2, HuffmanNode &parent)
{
parent.leftchild=ht1;
parent.rightchild=ht2;
parent.data = ht1->data+ht2->data;
}
HuffmanTree::HuffmanTree (float w[ ] , int n )
{
MinHeap<HuffmanNode> hp;
int i ;
HuffmanNode parent , *first, *second,work;
for ( i = 0; i < n; i++ )
{
work.data=w[i];
work.leftchild =work.rightchild = work.parent=0;
hp.Insert(work);
} //传送初始权值
for ( i = 0; i < n-1; i++ )
{
//建立哈夫曼树的过程,做n-1趟
first=new HuffmanNode;
second=new HuffmanNode;
hp.RemoveMin ( *first ); //选根权值最小的树
hp.RemoveMin ( *second ); //选根权值次小的树
if (first->leftchild) first->leftchild ->parent =first;
if (second->leftchild) second->leftchild ->parent =second;
mergeTree( first, second,parent ); //合并
hp.Insert ( parent ); //重新插入到小根堆中
}
root=new HuffmanNode;
*root=parent; //最后的结点为根结点,传值
}
第二种方式:
首先初始化
思路:用数组,先建立一个最小堆,
便于找到最小和次小
然后合并,
这里需要一个merge函数。
//构造函数
template <typename T, typename E>
HuffmanTree<T, E>::HuffmanTree(E w[], int n)
{
//给出n个权值w[1]~w[n], 构造Huffman树
MinHeap<T, HuffmanNode<T, E>> hp(20);
HuffmanNode<T, E>* parent=0, *first, *second, work;
HuffmanNode<T, E>* NodeList/* = new HuffmanNode<T,E>[n]*/;
//不宜一次性建立森林,否则析构函数一个一个删结点时操作系统断言出错
int i;
for (i = 0; i < n; i++) {//按棵逐步建立森林中的树木,并作为Huffman树的叶结点。数据放入森林
NodeList = new HuffmanNode<T, E>;
NodeList->data = w[i + 1];
NodeList->leftChild = NULL;
NodeList->rightChild = NULL;
NodeList->parent = NodeList;//父指针指向自己,信息入堆后,出堆时可以找到对应结点
hp.Insert(*NodeList); //森林信息插入最小堆中
}
for (int i = 0; i < n - 1; i++)
{
hp.RemoveMin(work);
first = work.parent; //first指向对应的最小结点,parent指向的是自己。
hp.RemoveMin(work); //根权值次小的树
second = work.parent; //second指向对应的次小结点
mergeTree(first, second, parent);
hp.Insert(*parent);
}
root = parent;
}
这里merge函数的定义
需要把孩子结点的父指针指向父节点。
父节点的数值是两个孩子结点值的和。
template <typename T, typename E>
void HuffmanTree<T, E>::mergeTree(HuffmanNode<T, E> *bt1,HuffmanNode<T, E>*bt2, HuffmanNode<T, E>*& parent)
{
//parent必须为引用,它作为返回量
//在函数中形成
parent = new HuffmanNode<T, E>;
parent->leftChild = bt1;
parent->rightChild = bt2;
parent->parent = parent;
parent->data = bt1->data + bt2->data;
bt1->parent = bt2->parent = parent;
}
第三种方式
用一个新的结构体的形式,保存值的大小,和哈夫曼结点的地址的指针。
struct node
{
float data;
HuffmanNode *link;
};
void HuffmanTree::mergeTree(HuffmanNode *ht1, HuffmanNode *ht2, node &node1)
{ HuffmanNode *parent;
parent=new HuffmanNode;
parent->leftchild=ht1;
parent->rightchild=ht2;
parent->data = ht1->data+ht2->data;
ht1->parent= ht2->parent=parent;
node1.data=parent->data;
node1.link=parent;
}
HuffmanTree::HuffmanTree (float w[ ] , int n )
{
MinHeap<node> hp;
int i ;
HuffmanNode *first, *second,*work;
node node1;
for ( i = 0; i < n; i++ )
{
work=new HuffmanNode;
work->data=w[i];
work->leftchild =work->rightchild = work->parent=0;
node1.data=w[i];
node1.link=work;
hp.Insert(node1);
} //传送初始权值
for ( i = 0; i < n-1; i++ )
{
//建立哈夫曼树的过程,做n-1趟
hp.RemoveMin ( node1 ); //选根权值最小的树
first=node1.link;
hp.RemoveMin ( node1 ); //选根权值次小的树
second=node1.link ;
mergeTree( first, second,node1 ); //合并
hp.Insert ( node1 ); //重新插入到小根堆中
}
root=node1.link; //最后的结点为根结点
}
将建好的树,输出到文件中。
template <typename T, typename E>
void HuffmanTree<T, E>::output(HuffmanNode<T, E>* t, string str, ostream& out) {//按目录结构方式输出二叉树,添加
if (!t) {
return;
}
out << str << t->data;
if (t->leftChild) {
out << "─┐" << endl;
if (t->rightChild) {
output(t->leftChild, str + "│ ", out);
}
else {
output(t->leftChild, str + " ", out);
}
}
if (t->rightChild) {
out << endl << str << "└─┐" << endl;
output(t->rightChild, str + " ", out);
}
}
int main()
{
int w[7] = { 3,4,5,2,1,7,9 };
string s;
HuffmanTree<int, int >hp(w,5);
hp.preorder(hp.getRoot());
ofstream out("CodeFile.txt", ios::binary);
hp.output(hp.getRoot(),s,out);
return 0;
}
哈夫曼编码
首先需要统计编码的字符种类以及个数。
有字符数组的集合之后,可以建立哈夫曼树。
进行编码
因为每一个字符对应一个编码
则这些编码可以保存到文件中。
再进行译码