哈夫曼编码
一、源程序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
/* Huffman 树的存储结构*/
#define n 8 /*叶子数目根据需要设定*/
#define m 2*n-1 /* Huffman 树中结点总数 */
typedef struct
{int weight; /*结点的权值*/
int lchild,rchild,parent; /*左、右孩子及双亲的下标*/
}htnode;
typedef htnode huffmantree[m+1];/* huffmantree是结构数组类型,其0号单元不用,存储哈夫曼树 */
typedef struct
{char ch; /*存储字符*/
char code[n+1]; /*存放编码位串*/
}codenode;
typedef codenode huffmancode[n+1];/*huffmancode是结构数组类型,其0号单元不用,存储哈夫曼编码*/
void inithuffmantree(huffmantree ht) /*初始化哈夫曼树函数inithuffmantree()*/
{int i;
for(i=0;i<=m;i++)
{ht[i].weight=0;
ht[i].lchild=ht[i].rchild=ht[i].parent=0;
}
}
void inputweight(huffmantree ht) /*输入权值函数 */
{int i;
for(i=1;i<=n;i++)
{printf("请输入第%d个权值: ",i);
scanf("%d",&ht[i].weight);
}
}
void selectmin(huffmantree ht, int i, int *p1, int *p2)
/* 在ht[1..i]中选两个权值最小的根结点,其序号为*p1和*p2,*p1中放权值最小的根结点的序号,*p2中放权值次小的根结点的序号*/
{int j,min1,min2; /* min1,min2分别是最小权值和次小权值*/
min1=min2=32767;
*p1=*p2=0;
for(j=1;j<=i;j++)
{if(ht[j].parent==0) /* j 为根结点*/
if(ht[j].weight<min1||min1==32767)
{
if(min1!=32767) {min2=min1; *p2=*p1;}
min1=ht[j].weight;
*p1=j;
}
else
if(ht[j].weight<min2||min2==32767)
{min2=ht[j].weight;
*p2=j;}
}
}
void createhuffmantree(huffmantree ht) /*构造huffman树,ht[m]为其根结点*/
{
int i,p1,p2;
inithuffmantree(ht); /* 将ht初始化*/
inputweight(ht); /* 输入叶子权值至ht [1..n]的weight域*/
for(i=n+1;i<=m;i++) /* 共进行n-1次合并,新结点依次存于ht[i]中*/
{selectmin(ht,i-1,&p1,&p2); /*在ht [1.. i-1]中选择两个权值最小的根结点,其序号分别为p1和p2*/
ht[p1].parent=ht[p2].parent=i;
ht[i].lchild=p1; /* 最小权值的根结点是新结点的左孩子*/
ht[i].rchild=p2; /* 次小权值的根结点是新结点的右孩子*/
ht[i].weight=ht[p1].weight+ht[p2].weight;
}
}
void huffmancodes(huffmantree ht,huffmancode hcd) /*根据huffman树ht求huffman编码*/
{
int c,p,i; /* c和p分别指示 ht中孩子和双亲的位置 */
char cd[n+1]; /* 临时存放编码*/
int start; /* 指示编码在cd 中的起始位置*/
cd[n]='\0';
getchar(); /* 编码结束符*/
printf("请输入字符");
for(i=1;i<=n;i++) /* 依次求叶子ht [i]的编码*/
{ hcd[i].ch=getchar(); /* 读入叶子ht [i]对应的字符*/
start=n; /* 编码起始位置的初值*/
c=i; /* 从叶子ht [i]开始上溯*/
while((p=ht[c].parent)!=0) /* 直至上溯到ht [ c]是树根为止*/
{ cd[--start]=(ht[p].lchild==c)?'0':'1'; /*若ht [ c]是ht[p]的左孩子,则生成代码0,否则生成代码1*/
c=p; /* 继续上溯*/
}
strcpy(hcd[i].code,&cd[start]); /* 复制编码位串*/
}
printf("\n");
for(i=1;i<=n;i++)
printf("第%d个字符%c的编码为%s\n",i,hcd[i].ch,hcd[i].code);
}
void main()
{huffmantree t;
huffmancode h;
printf("\n请输入%d个权值\n",n);
createhuffmantree(t); /* 构造huffman树*/
huffmancodes(t,h); /* 构造huffman编码*/
}
运行结果
心得
getch与getchar基本功能相同,差别是getch直接从键盘获取键值,不等待用户按回车,只要用户按一个键,getch就立刻返回, getch返回值是用户输入的ASCII码,出错返回-1.输入的字符不会回显在屏幕上.
getch函数常用于程序调试中,在调试时,在关键位置显示有关的结果以待查看,然后用getch函数暂停程序运行,当按任意键后程序继续运行.
#include<iostream>
using namespace std;
struct htnode //哈夫曼树结点结构定义
{
char ch; //结点值
int weight; //结点权值
int parent; //父结点下标
int lchild,rchild; //左、右孩子结点下标
};
class huffmantree //哈夫曼树类定义
{
public:
void code(char str1[],int w[],int n); //编码
void uncode(char str1[],char str2[],int n); //解码
private:
htnode ht[3000]; //用数组存储哈夫曼树
void creathuffmantree(char str1[],int w[],int n); // 创建哈夫曼树
void select(int pos,int &r1,int &r2);
};
//在数组ht中从0到pos位置,查找未加入哈夫曼树的权值最小的两个数的位置r1,r2
//r1为权值最小的位置,r2为权值第二小的位置
void huffmantree::select(int pos,int &r1,int &r2)
{
r1=r2=0; //初始化为0位置
for(int i=1;i<=pos;i++)
{
if(ht[i].parent!=0) //父结点不等于0,表示已加入哈夫曼树
continue; //继续向前查找
if(r1==0) //把r1设置为1
r1=i;
else if(r2==0) //把r2设置为2
r2=i;
//从i等于3开始比较。我认为这里程序有问题,无法得到权最小的两位置,你自己再改改
else if(ht[i].weight<ht[r1].weight) //如小于r1的权则设置r1为i
r1=i;
else if(ht[i].weight<ht[r2].weight) //如大于r1的权,再和r2的权比较
r2=i;
}
}
// 创建哈夫曼树,str1[]为结点值数组,w[]为权值数组,共n个结点
void huffmantree::creathuffmantree(char str1[],int w[],int n)
{
int pos;
//把结点值和权值复制到数组ht
for(pos=1;pos<=n;pos++) //注意没有使用ht数组位置为0的空间
{
ht[pos].weight=w[pos-1]; //结点权值
ht[pos].ch=str1[pos-1]; //结点值
ht[pos].parent=ht[pos].lchild=ht[pos].rchild=0; //父结点为0表示未加入哈夫曼树,左右孩子都为0表示叶子结点
}
//这是构造哈夫曼树的过程,即设置数组ht的其他结点
for(pos=n+1;pos<=2*n-1;pos++) //注意用n个数值构造的哈夫曼树有2*n-1个结点
{
int r1,r2;
select(pos-1,r1,r2);
ht[r1].parent=ht[r2].parent=pos; //把r1,r2结点加入哈夫曼树,并设置他们的父结点位置为pos
ht[pos].lchild=r1; //设置pos结点的左孩子为r1
ht[pos].rchild=r2; //设置pos结点的右孩子为r2
ht[pos].weight=ht[r1].weight+ht[r2].weight; //设置pos结点的权值为左右孩子权值的和
ht[pos].parent=0; //其父结点为0
}
}
//求哈夫曼编码
void huffmantree::code(char str1[],int w[],int n)
{
creathuffmantree(str1,w,n); //建立哈夫曼树
int start,c,i,f,j;
char *cd;
cd=new char[n]; //用于保存哈夫曼编码
cd[n-1]='\0'; //设置字符串结束符
for(i=1;i<=n;i++) //分别输出n个结点的哈夫曼编码
{
start=n-1; //注意从cd数组的末尾向前加入编码
for(c=i,f=ht[i].parent;f!=0;c=f,f=ht[c].parent) //从叶子结点往上走到根结点为止
{
//左0,右1
if(ht[f].lchild==c)
cd[--start]='0';
else
cd[--start]='1';
}
cout<<"结点"<<str1[i-1]<<"的编码为:"<<endl;
for(j=start;j<n;j++) //输出所有叶子结点
cout<<cd[j];
cout<<endl;
}
delete []cd; //撤销
}
//翻译哈夫曼编码,str1[]为结点值,str2[]为哈夫曼编码,共n个结点
//输出哈夫曼编码为str2的结点值
void huffmantree::uncode(char str1[],char str2[],int n)
{
int i,f;
char c;
for(i=0;i<strlen(str2);) //从根结点往下走到叶子结点
{
//根结点在数组ht最后一个位置
//从上往下也是左0右1
for(f=2*n-1;ht[f].lchild!=0&&ht[f].rchild!=0;)
{
c=str2[i];
if(c=='1') //等于1往右走
{
f=ht[f].rchild;
i++;
}
else //等于0往左走
{
f=ht[f].lchild;
i++;
}
}
cout<<ht[f].ch; //输出找到的叶子结点
}
cout<<endl;
}
//这个不用注释了吧
int main()
{
char str[27],str2[3000],c;
char ch[]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
int cd[27],s,len,i,w[27];
sb1:
cout<<"请输入要编码的字符串"<<endl;
cin>>str;
huffmantree obj;
s=0;
memset(cd,0,sizeof(cd));
len=strlen(str);
for(i=0;i<len;i++)
{
cd[str[i]-'a']++;
}
for(i=0;i<26;i++)
{
if(cd[i])
{
str[s]=ch[i];
w[s]=cd[i];
s++;
}
}
cout<<"各节点编码如下:"<<endl;
obj.code(str,w,s);
sb2:
cout<<"请输入要解码的01串"<<endl;
cin>>str2;
cout<<"解码结果如下:"<<endl;
obj.uncode(str,str2,s);
cout<<"是否继续解码?(Y/N)"<<endl;
getchar();
c=getchar();
if(c=='Y')
goto sb2;
cout<<"是否继续编码?(Y/N)"<<endl;
getchar();
c=getchar();
if(c=='Y')
goto sb1;
return 0;
}