前面一个博客先行测试学习了怎么构造二叉树,怎么构造堆结构以及怎么实现优先权队列,这三个数据结构类型为使用c++实现哈夫曼编码打下了基础,哈夫曼树的构造在以上几个数据结构掌握之后应该没有大问题了,剩下的唯一一个实现哈夫曼编码的问题就是如何按照生成的哈夫曼树给值进行编码,用眼睛看的时候很简单,只要左右左右下去就能数出来,但是用程序实现就有带你难度了,为了解决这个问题我先进行了一个哈夫曼编码测试实验
1.计算权值补充实验
#include "iostream"
using namespace std;
struct code
{
char code1;
int num;
};
void main()
{
code qz[100];
for(int i=0;i<100;i++)
{
qz[i].num=0;
}
char *str=new char[100];
gets(str);
i=0;
int j=0;
bool adjust=0;
while(str[i]!='\0')
{
for(j=0;qz[j].num!=0;j++)
{
if(str[i]==qz[j].code1)
{
adjust=1;
break;
}
}
if(adjust)
{
qz[j].num++;
}
else
{
qz[j].code1=str[i];
qz[j].num++;
}
adjust=0;
i++;
}
for(i=0;qz[i].num!=0;i++)
{
cout<<qz[i].code1<<' '<<qz[i].num<<endl;
}
}
通过十进制的乘除算法得到类哈夫曼编码(root=1 左*2,右*2+1 叶子返回/2
非跟节点返回/2)再10进制转2进制(itoa(int,char,2))得到二进制形式的哈夫曼编码
2.哈夫曼编码源码
#include "BinaryTree.h"
#include "PrioQueue.h"
template <class T>
class HfmTree:public BinaryTree<T>
{
private:
T weight;
public:
operator T()const //hfmtree对象隐式转换T类型时重载
{
return weight;
}
T getW()
{
return weight;
}
void putW(const T& x)
{
weight=x;
}
void SetNull()
{
root=NULL;
}
HfmTree<T> CreateHfmTree(T w[],int n);
~HfmTree<T>()
{
}
};
template <class T>
HfmTree<T> HfmTree<T>::CreateHfmTree(T w[],int n)
{
PrioQueue <HfmTree<T> > pq(n);
HfmTree<T> x,y,z,zero;
for(int i=0;i<n;i++) //将哈弗曼树压入最小优先权队列
{
z.MakeTree(w[i],x,y);
z.putW(w[i]);
pq.Append(z); //每次压入都会排序
z.SetNull();
}
for(i=1;i<n;i++)
{
pq.Serve(x);
pq.Serve(y); //取出最小的两个数
z.MakeTree(x.getW()+y.getW(),x,y);
z.putW(x.getW()+y.getW());
pq.Append(z);
z.SetNull();
}
pq.Serve(z);
return z;
}
**通过本次试验二叉树的基本操作及哈夫曼编码译码系统的实现。不断完善代码和调试的过程中提升了编码能力和出错定位调试的速度。尤其是后者相比于第一次试验有了质的提升。
花时间最长的部分是哈夫曼编码部分,如何将构造好的哈弗曼树编码与输入的字符相对应和如何将人眼显而易见的哈夫曼编码用字符串的形式表示出来是最难的两个部分。
将各个模块均要直接访问的元素或者数组定义为static放在main函数外面。并在main函数开始的时候初始化。
Getcommand函数出了个小问题就是cin这段代码经常会被跳过。下断点调试过后定位到了问题所在。突然想到之前学习c的时候有说过回车键会被放入输入缓存中而导致下一次输入直接被跳过。查阅资料使用了cin.clear();//重置错误输入+cin.sync();//清空缓冲区解决了这个问题。
计算权值是一个较难部分使用了之前不怎么用的break跳出循环这个方法,加上自定义了一个结构体并定义了这个结构体的数组实现了哈夫曼字符和编码的储存。解决了前面提到的的哈弗曼树编码与输入的字符相对应这个难点。
从哈弗曼树解析出哈夫曼编码这个问题我借鉴了先序遍历的算法,通过判断左右子树是否同时为空的方式找到叶子节点。通过十进制的乘除算法得到类哈夫曼编码(root=1 左*2,右*2+1 叶子返回/2 非跟节点返回/2)再10进制转2进制(itoa(int,char,2))得到二进制形式的哈夫曼编码。
输入字符串使用char 字符串数组并cin方式输入
哈夫曼译码系统关键是匹配二进制字符串,使用memcpy逐个增加复制个数匹配字符。匹配完成后再利用memcpy消去匹配过了的二进制字符串并进行下一个字符的匹配。
这次main函数主要写了流程,具体的实现都放在了main函数外面: ShowMenu dectobin search showwithcode precodeshowHfmTree calcodesweight getqzi
getCommand HfmTree builduphfmtree这十个函数,简化了代码**