哈夫曼树的创建和编码
项目忙的要死,博客停了两天,做外包的真不好受,还是做产品的强。软件最后最值钱的不是代码,而是相关的文档,文档清楚,依葫芦画瓢照做出来应该不难。项目结束了至少要整理出需求规格说明书,系统设计文档,用户使用说明书,开发进度表,投标书,工作说明书等文档。
本文根据《数据结构与算法》(C语言版)(第三版) 整理。
哈夫曼树的结点类型声明:
struct TreeNode { int weight; int parent; int lchild; int rchild; }; typedef struct TreeNode HFTreeNode; typedef HFTreeNode HuffmanTree;
哈夫曼树的构造算法:
#define MaxSize 1000 //叶子数目 void Select(HuffmanTree *HT, int g, int &s1, int &s2); void CreateHuffmanTree(HuffmanTree T[MaxSize], int n) { int i,p1,p2; if(n<1) return 1; m=2*n; //计算哈夫曼树的结点大小 for(i=1; i<m; i++) { T[i].parent=0; T[i].lchild=0; T[i].rchild=0; T[i].weight=0; } for(i=1; i<n; i++) //读入叶子结点的权值 { scanf("%d",&weight); T[i].weight=weight; } for(i=n; i<m-1; i++) { SelectMin(T, i-1, p1, p2); //在T[0...i-1]中选择两个权值最小的根结点,其序号分别为p1和p2 T[p1].parent=T[p2].parent=i; T[i].lchild=p1; //最小权的根结点是新结点的左孩子 T[i].rchild=p2; //次小权的根结点是新结点的右孩子 T[i].weight=T[p1].weight+T[p2].weight; } } void selectMin(HuffmanTree *HT, int g, int &s1, int &s2) { int j, k, m, n; for(k=1; k<=g; k++) //找到一个parent为-1的子树 { if(HT[k].parent==0) { s1=k; break; } } for(j=1; j<=g; j++) { if((HT[j].weight<=HT[k].weight)&&(HT[j].parent==0)) //找到一个parent为-1权值最小的子树 s1=j; } for(m=1; m<=g; m++) { if((HT[m].parent==0)&&(m!=s1)) { s2=m; break; } } for(n=1; n<=g; n++) { if((HT[n].weight<HT[m].weight)&&(HT[n].parent==0)&&(n!=s1)) s2=n; } }
struct CodeNode
{
char ch; //存储字符
char bits[n+1]; //存放编码位串
};
typedef struct CodeNode CodeNoe;
typedef CodeNoe HUffmanCode[n];
哈夫曼编码的算法:
void CharSetHuffmanEncoding(HuffmanTree T, HuffmanCode H)
{ //根据哈夫曼树T求哈夫曼编码表H
int c, p, i;
char cd[n+1];
int start;
cd[n]='\0';
for(i=0; i<n; i++)
{
//依次求叶子T[i]的编码
H[i].ch=getchar(); //读入叶子T[i]对应的字符
start=n; //编码起始位置的初值
c=i; //从叶子T[i]开始上溯
while(p=T[c].parent>0)
{
if(T[p].lchild==c)
{
cd[--start]='0';
}
else
{
cd[--start]='1';
}
c=p; //继续上溯
}
strcpy(H[i].bits, &cd[start]); //复制编码位串
}
}
void CharSetHuffmanDecoding(HuffmanTree T, char* cd, int n) { int p=2*n-2; //从根结点开始 int i=0; //当要解码的字符串没有结束时 while(cd[i]!='/0') { //当还没有到达哈夫曼树的叶子并且要解码的字符串没有结束时 while((T[p].lchild!=0 && T[p].rchild != 0) && cd[i] != '\0') { if(cd[i] == '0') { //如果是0,则叶子在左子树 p=T[p].lchild; } else { //如果是1,则叶子在左子树 p=T[p].rchild; } i++; } //如果到达哈夫曼树的叶子时 if(T[p].lchild == 0 && T[p].rchild == 0) { printf("%c", T[p].ch); p = 2*n-1; } else //如果编号为p的结点不是叶子,那么编码有错 { printf("\n解码出错! \n"); return; } } printf("\n"); }
// HuffmanTree.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct HuffmanTree { int weight; int parent, lchild, rchild; } HuffmanTree; typedef struct CodeNode { int ch; char bits[4+1]; }CodeNode; void SelectMin(HuffmanTree tree[], int len, int * pos1, int* pos2) { int min=255; int i, j; *pos1=0; *pos2=0; for(i=0; i<len; i++) { if(tree[i].parent==-1) if(min>tree[i].weight) { min=tree[i].weight; *pos1=i; } } min=255; for(j=0; j<len; j++) { if(j==*pos1) continue; if(tree[j].parent==-1) if(min>tree[j].weight) { min=tree[j].weight; *pos2=j; } } } void CreateHuffmanTree(HuffmanTree tree[], int n) { int m=2*n; int i; for(i=n; i<m-1; i++) { int pos1, pos2; HuffmanTree node; SelectMin(tree, i, &pos1, &pos2); printf("pos1=%d,pos2=%d\n", pos1, pos2); node.weight=tree[pos1].weight+tree[pos2].weight; tree[pos1].parent=i; tree[pos2].parent=i; node.lchild=pos1; node.rchild=pos2; node.parent=-1; tree[i]=node; } } void HuffmanEncoding(HuffmanTree tree[]) { int c, p, i; int start; char cd[4+1]; cd[4]='\0'; for(i=0; i<4; i++) { printf("\n"); printf("%d",tree[i].weight); printf(":"); start=4; c=i; while((p=tree[c].parent)!=-1) { if(tree[p].lchild==c) { cd[--start]='0'; } else { cd[--start]='1'; } c=p; } printf(&cd[start]); } } int main(int argc, char* argv[]) { HuffmanTree tree[4*2]; int i, j; for(i=0; i<4; i++) { tree[i].lchild=-1; tree[i].rchild=-1; tree[i].parent=-1; } printf("请输入哈夫曼树叶子结点的权值: \n"); for(i=0; i<4; i++) //读入叶子结点的权值 { int weight; scanf("%d",&weight); tree[i].weight=weight; } CreateHuffmanTree(tree, 4); for(j=0; j<2*4-1; j++) { printf("tree[%d]:weight=%d \n", j, tree[j].weight); } HuffmanEncoding(tree); return 0; }
Ctrl+F5执行HuffmanTree.cpp结果如下图: