学过数据结构自然清楚哈夫曼树是每次选择两个最小权值的结点构树,一般来说如果只是要求最小帯权路径长度,不一定要构造哈夫曼树,直接用一个优先队列就可以模拟这个过程,但是如果需要实现哈夫曼编码,这时就必须得构建哈夫曼树了。
由定义哈夫曼树是二叉树,那分析,构建哈夫曼树和普通的二叉树有什么区别,首先哈夫曼树的大小是2*n-1,大小是定的,那用静态二叉树来描述更方便,可以前n个数都是叶子节点;其次由于哈夫曼树叶子结点很明确,那构建哈夫曼编码的时候可以自结点向根,那最好定义一个父亲结点,方便自下向上寻根。
综上哈夫曼树定义如下:
struct node{ //哈夫曼树的结构,采用静态二叉树 int weight; int parent; int lchild,rchild; } Node[N]; //定义一个足够大的静态哈夫曼树
了解结构后,现在考虑,怎样定义优先队列,优先队列是定义成node型,还是其他?实际上,我们每次从优先队列拿出两个结点后,需要修改结点,又考虑到优先队列存储的只是保存数据的副本,为了便于修改,可以将其定义成int型,存放哈夫曼树的下标,注意优先队列默认是从大到小排列,这里需要修改。
综上,简单的实现哈夫曼树、哈夫曼编码如下:
#include<iostream> #include<queue> #include<string> using namespace std; #define N 1000 struct node{ //哈夫曼树的结构,采用静态二叉树 int weight; int parent; int lchild,rchild; } Node[N]; //定义一个足够大的静态哈夫曼树 struct cmp{ //将优先队列按从小到大排列的比较函数 bool operator ()(int a,int b){ return Node[a].weight>Node[b].weight; } }; priority_queue<int,vector<int>,cmp>q;//弄成int型方便修改Node里的值 string s[100]; void createhuff(int n){ //创建哈夫曼树 for(int i=1;i<=n;i++) q.push(i); int i,j,m=n+1; while(q.size()>1){ i=q.top();q.pop(); j=q.top();q.pop(); Node[m].weight=Node[i].weight+Node[j].weight; Node[m].lchild=i;Node[m].rchild=j; Node[i].parent=Node[j].parent=m; q.push(m++); } } void preorder(int root){ //前序遍历 if(root==0) return; printf("%d ",Node[root].weight); preorder(Node[root].lchild); preorder(Node[root].rchild); } void encodehuff (int n) {//生成编码,从下往上 for(int i=1;i<=n;i++){ int p=Node[i].parent,k=i; string str; while(p){ if(Node[p].lchild==k){ //这里生成的是逆向代码 str+="1";} else { str+="0";} k=p; p=Node[k].parent; } for(int f=str.length();f>0;f--) //将其编码正向 s[i]+=str[f-1]; } } void printfcode(int n) { for(int i=1;i<=n;i++) printf("%s\n",s[i].c_str()); //这里用printf输出字符串需要将它转化为字符数组,用cout不用 } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ //从1开始,0值与空对应 scanf("%d",&Node[i].weight); Node[i].parent=Node[i].lchild=Node[i].rchild=0; } createhuff(n); encodehuff(n); printfcode(n); return 0; }
上面只是简单实现,具体应用可以添加一些输入输出,改变参数形式等。