哈夫曼编码

来源:互联网 发布:linux 安装nginx 编辑:程序博客网 时间:2024/06/11 22:34

这是我第一篇,之前写这个真是一脸懵,只记得我9点写到第二天四点  我不喜欢我的程序因为用户输入了一些特殊的情况就挂  所以一个程序无论多小我都会加上排错的函数。

这个代码可以输出哈夫曼形成的二叉树(好像不能称为树,管他呢)。感觉还是不错的··~



不多说上代码:

# include <stdio.h># include <stdlib.h># include <string.h># define N 1001  //如果想录入x个字节那么就把N的数值改成x+1  x>3# define M 13          //哈夫曼树编码的长度typedef struct ptree              //定义二叉树结点类型{struct ptree *lchild;     //左0右1子结点指针struct ptree *rchild;     //右子结点指针int w;         //w[2]存储节点权值char zha[2];       }Ptree,*Optree;     //optimum tree typedef struct pforest              //每个此类结构体连接一个二叉树与另一个此类结构体{struct pforest *link;       //连接每个叶子节点节点权值由大到小排列struct ptree *root;         //指向叶子节点}Forest,*forest;int gainchar(char *a,int min,int max);//返回字符长度int jianyan(char *ch,int type[3]);//检验字符串是否合法合法的话type[0]=大写字母个数,type[1]=小写字母个数,type[2]=数字字符个数int BFS(char *a,char *b);//字符串匹配返回匹配的个数//我还有返回所有匹配的下表的函数int zhengli(char b[],char *p,int *q);//整理字符串,所有可以出现的字符整理到p里,p[i]的次数储存在q[i]中,返回字符串中不同的个数Optree hafman(int n,char m[],int w[]);//构造哈夫曼树,权值存在w[]中,返回树根pforest *inforest(forest f,Optree t);//将每个二叉树连接起来.根据每个二叉树的根节点的权值大小,将二叉树权值由大到小相接void shuxing(Optree &p,int len);//输出树的形状,形参len=0;void tiaozheng(int a);//根据位数调整枝杈长度,嵌套在shu函数里,主函数中不调用int bianma(Optree p,int a,char b[]);//对哈夫曼树进行编码,返回哈夫曼树的权值void main(){    int a=0,d,k;char b[N],*p=NULL;char kkk[M]={"\0"};int type[3]={0,0,0},*q=NULL;//type[0]=大写字母个数type[1]=小写字母个数type[2]=数字字符个数    Optree head;  do{printf("输入字符串<只录入字母或数字>(2--%d)字节:",N-1);d=gainchar(b,2,N);   //d=b的字符长度}while(!jianyan(b,type));  //检验不合法时返回0q=(int *)calloc(d,sizeof(int));   //申请    p=(char *)calloc(d+1,sizeof(char));  //申请k=zhengli(b,p,q);  //整理字符串printf("大写字母:%d小写字母:%d数字:%d 共%d个字符 字符种类%d 种\n注:X<<Y+<<   X为出现的次数 Y为字符!\n",type[0],type[1],type[2],d,k);head=hafman(k,p,q);   //构造哈夫曼树printf("①--②--③--④--⑤--⑥--⑦--⑧--⑨--⑩--①--②\n");shuxing(head,0);  //显示树的形状    printf("总权值WPL=%d",bianma(head,0,kkk));getchar();//暂停一下free(p);free(q);        //此处可以改造一下bianma()函数让其释放哈夫曼树,我就不改了有时间再改}Optree hafman(int n,char m[],int w[])//构造哈夫曼树,权值存在w[]中,返回树根{forest p1,p2,froot;Optree leaves,t;froot=(forest)malloc(sizeof(Forest));//建立一个根节点froot->link=NULL;            while(n-->0)          //产生n个叶子{leaves=(Optree)malloc(sizeof(Ptree));//开辟新的叶子结点leaves->w=w[n];                      //给叶子结点赋权值leaves->zha[0]=m[n];                //将字符存在叶子节点里leaves->rchild=leaves->lchild=NULL; //叶子初始化froot=inforest(froot,leaves);  //按权值从大到小的顺序将叶子结点从froot树根向下排列,权值小的放在最后     }while(((froot->link)->link)!=NULL)//上面将叶子进行了初步排列,之后要对叶子进行合并,从树根开始两个两个的进行合并{p1=froot->link;     //p1指向第一片叶子p2=p1->link;     //p2指向第二片叶子  经过inforest排列后权值p1>=p2froot->link=p2->link;              //froot指向第三片叶子t=(Optree)malloc(sizeof(Ptree));  //开辟新的结点t->w=(p1->root->w)+(p2->root->w);         //权相加t->lchild=p1->root;            //将叶子整合到t上t->zha[0]='<';t->rchild=p2->root;             //产生新二叉树froot=inforest(froot,t);          //将新的二叉树t整合到树干上free(p1);                 //释放p1所指的内存free(p2);                 //释放p2}t=froot->link->root;            //当froot指向的最后一个节点link时此时的link节点已经是全部排列好的hafuman树free(froot);                //去掉froot留下树叉return(t);                  //返回二叉树的树根}pforest *inforest(forest f,Optree t)//将每个二叉树连接起来.根据每个二叉树的根节点的权值大小,将二叉树权值由大到小相接{     forest p,q,r;r=(forest)malloc(sizeof(Forest)); //开辟新的树干结点r->root=t;                   //将其叶子指针指向新加入的叶子tq=f;                         //q指向根p=f->link;                   //p指向根的下一个位置    while (p!=NULL)            //当整个树干没有到头时{if(t->w> p->root->w)          //如果t的权值大于ti的权值{q=p;                   //q移到p的位置p=p->link;             //p向后寻找}elsep=NULL;                  //新插入的t<=某个位置的权,将在位置插入此节点,跳出while循环}r->link=q->link;            //r的下个节点指向q的后序节点q->link=r;                 //r接在q的后面return(f);                 //返回树根f}void shuxing(Optree &p,int len)//输出树的形状,形参len=0;{int i;if (p!=NULL){shuxing(p->rchild,len+1);   //递归for (i=1;i<=4*len;i++)  printf("%c",i%4-1?' ':'|');printf("%d<%c",p->w,p->zha[0]); //输出数据tiaozheng(p->w);    //根据数据的大小调整树叉的长度shuxing(p->lchild,len+1);   //递归}}void tiaozheng(int a)//根据位数调整枝杈长度,嵌套在shu函数里{char cha[4][6]={"<--+<","<+<","<<","<"};int k=0;while(a){ a/=10;      k++;}printf("%s\n",cha[k]);}int gainchar(char *a,int min,int max)//对*a输入范围[min,max-1]{                            int c,k;do{c=-1;k=0;fgets(a,max,stdin);   while(a[++c]);c=a[c-1]=='\n'&&c<max?c-1:c;if(c>=max-1)while(getchar()!='\n')   //判断缓冲区是否还有未读取的字符k++;elsea[c]='\0';     if(k||c&&c<min)printf("输入长度有误,请重新输入!\n注:只录入(%d--%d)字节:\n",min,max-1);}while(k||c<min);    return c;}int jianyan(char *ch,int type[3])//判断字符是否合法,合法返回1,不合法返回0{char a[][3]={"AZ","az","09"};//对可能出现的字符进行判断int b[2]={0,0},i,j;b[0]=strlen(ch);for(i=0;i<b[0];i++){for(j=0;j<3;j++)if(ch[i]<=a[j][1]&&ch[i]>=a[j][0]){b[1]++;                type[j]++;}if(!b[1]){printf("输入的字符中有除字母和数字以外的字符!\n请重新");type[0]=type[1]=type[2]=0;return 0;}else b[1]=0;}return 1;}int BFS(char *a,char *b)//a为主串,b为被检验的串,d为匹配的下标,经典算法,不再写注释{                     int c[2];int i=0,j=0,k=0;c[0]=strlen(a);c[1]=strlen(b);    //c[0]c[1]存长度while (i <=c[0])if(a[i]==b[j]&&j<c[1]){i++;j++;}else{ if(j==c[1]) k++; elsei-=j-1; j=0;}return k;            //返回匹配的个数k}int zhengli(char b[],char *p,int *q)    //对重复的进行整理{int i,k,length;char spot[2]={"\0"};        length=strlen(b);  for(i=0,k=0;i<length;i++){    spot[0]=b[i];        //每个字符串复制到spot中进行字符串匹配if(!BFS(p,spot))    //如果匹配失败,则说明此字符串第一次出现,就将其放在数组p里 {q[k]=BFS(b,spot);   p[k++]=spot[0]; }}return k;     //返回不相同的总个数}int bianma(Optree p,int a,char b[])  //初始化a=c=0;{ int static c=0;        //设置静态变量c    if(p)      {          if(p->lchild)           //是左侧{b[a]='0';       //左零右一            bianma(p->lchild,a+1,b);  //继续遍历}        if(p->rchild){b[a]='1';       //左零右一            bianma(p->rchild,a+1,b);    //继续遍历}   if(p->zha[0]!='<')        //如果是树叶{    b[a]='\0';             printf("%-13s\"%c\"      %03d次\n",b,p->zha[0],p->w); //输出编码和字符c+=a*p->w;             //计算权值}    } return c;           //返回静态变量}/*制作者zha good well输入一段字符串对字符串的字符种类和个数进行整理,并输出编码值哈夫曼编码*/


3 0