哈夫曼编码

一、源程序

#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;
}