⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 huff.c

📁 Huffman编解码完整代码
💻 C
字号:
// 霍夫曼树_01051312杨富强.cpp : Defines the entry point for the console application.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#define MAX_BUFFER 1024  //字符串最大长度
#define MAX_LEN 15   //单个字符的代码最大长度
#define MAX_PATH 80
#define TOKEN " \t\n\r"
#define DATA "fq.dat"


typedef struct lnode  //带权字符节点定义
{ char data;      //字符
  float info;    //权值
  struct lnode *lchild,*rchild,*parent;
} letter;//未建树之前用单链表存储(按权值由小到大,且parent指向下一节点)
 
typedef struct cnode  //已编码字符的节点定义
{ char data;         //字符
  char code[MAX_LEN];   //代码
  struct cnode *next;
} codenode;


letter *root=NULL;   //霍夫曼树的根节点
codenode *first=NULL;//已编码字符库(单链表)的头节点
codenode *q=NULL;    //已编码字符库(单链表)的活动节点
                     //以上三个全局变量所有函数都可引用


letter* GetInfo();   //将用户输入的带权字符,按权值由小到大建立单链表,返回头节点
void Insert(letter *p,letter *lnew);//将节点lnew按权值大小插入到链表p中
letter* CreateTree(letter *head);//将字符单链表head建立霍夫曼树,返回根节点
void MakeCode(letter *root);//为霍夫曼树root的所有叶子节点编码
void Translate(char *mess,int mode);//字符串与代码串互相翻译
void Operator(); //  



/*======================================================================*/
int main(int argc, char* argv[])
{
    letter *head=NULL;//未建树字符链表的头节点(不存字符)
    head=GetInfo();    //建立带权字符的单链表,要求用户输入带权字符
    root=CreateTree(head);//将带权字符的单链表head建立霍夫曼树
    first=(codenode*)malloc(sizeof(codenode));//已编码字符库(单链表)的头节点(不存字符)
	first->next=NULL;//初始化
    q=first;         //初始化
    MakeCode(root);  //编码
    q->next=NULL;

    //以下打印输出编码
    q=first->next;//因为头节点不存字符
    while(q)
    {  
       fprintf(stderr,"\n字符 %c 的代码是 %s",q->data,q->code);
       q=q->next;
	}
    fprintf(stderr,"\n=======================编码结束========================\n\n");

    Operator(); //以下解码或编码
    return 0;
}


letter* GetInfo()  //将用户输入的带权字符,按权值由小到大建立单链表,返回头节点
{ 
   letter *r,*pnew;//r为单链表头节点,pnew为新节点
   char buf[MAX_LEN]; 
   char * args[20];  // pointers to arg strings
   char ** arg;            // working pointer thru args   	
   FILE *openfile;

   r=(letter*)malloc(sizeof(letter));
   r->parent=NULL;    //初始化

   if( (openfile=fopen(DATA,"r"))==NULL )
   {
	    fprintf(stderr,"\nCannot open file: fq.txt \n");
	    exit(0);	 
   }
      
   while(!feof(openfile))
   { 
	  if (fgets(buf, MAX_BUFFER, openfile))
	  {  
             arg = args;
			 *arg++ = strtok(buf,TOKEN);   // tokenize input
             while ((*arg++ = strtok(NULL,TOKEN))); // last entry will be NULL 
             if(args[0]==NULL)
				 continue;
             pnew=(letter*)malloc(sizeof(letter)); //新建空节点

			 if(!strcmp(args[0],"\\1"))
			     pnew->data=' ';
			 else if(!strcmp(args[0],"\\t"))
                 pnew->data='\t';
             else if(!strcmp(args[0],"\\n"))
                 pnew->data='\n';
			 else if(!strcmp(args[0],"\\r"))
                 pnew->data='\r';
             else 			  
				 pnew->data=args[0][0];

			 pnew->info=(float)atof(args[1]) ; //带权字符
             pnew->lchild=NULL;
             pnew->rchild=NULL;
        	 pnew->parent=NULL;
             Insert(r,pnew);  //按权值大小插入单链表
	  }
   }
   fclose(openfile);
   return r;    //返回头节点
}


void Insert(letter *p,letter *lnew)//将节点lnew按权值大小插入到链表p中
{  
   while(p->parent)
   { 
      if(lnew->info<p->parent->info)//比较权值
	  { 
		lnew->parent=p->parent;
        p->parent=lnew;
        return;
	  }
      p=p->parent;
   }
   //若比所有节点权值大,插到尾部
      p->parent=lnew;
      lnew->parent=NULL;
      return;
} 



letter* CreateTree(letter *head)//将带权字符的单链表head建立霍夫曼树
{  letter *pnode=NULL;//指向子树根节点
   while(head->parent->parent)//当单链表中除头节点外只剩一个节点,建树完毕
   {
      pnode=(letter*)malloc(sizeof(letter));
      pnode->lchild=head->parent;
      pnode->rchild=head->parent->parent;
      head->parent=pnode->rchild->parent;//摘掉单链表的前两个节点,作为根节点的左右儿子
      pnode->info=pnode->lchild->info+pnode->rchild->info;
      pnode->lchild->parent=pnode->rchild->parent=pnode;
	  pnode->parent=NULL;
      Insert(head,pnode);//将子树根节点插入单链表
   }
   pnode=head->parent;
   return pnode;//返回根节点
}

void MakeCode(letter *p)//为霍夫曼树root的所有叶子节点编码
{  static char temp[MAX_LEN];//在函数递归调用过程中,都可使用其值
   static int top=-1;//在函数递归调用过程中,都可使用其值
   
   if(p->lchild)           //有左子树
   {   temp[++top]='0';    //入栈
       MakeCode(p->lchild);//递归
       temp[top]='1';      //修改栈顶
       MakeCode(p->rchild);//递归
       top--;              //退栈
   }
    else //无左子树,即为叶子节点
    {  q->next=(codenode*)malloc(sizeof(codenode));
	   q=q->next;
	   q->data=p->data;
//	   strset(q->code,0);//置空
	   q->code[0]=0;
       strncpy(q->code,temp,top+1);
	   q->code[top+1]=0;
    }//新建编码节点,并插到单链表中
    return;
}



void Translate(char *mess,int mode)//字符串与代码串互相翻译
{
  int i;
  char trans[MAX_BUFFER*MAX_LEN];
  codenode *q;
//  fprintf(stderr,"mark 1: mode=%d mess=%s### codes=%s### \n",mode,mess,codes); ///////////////////////////////////
  if(mode==0)//加密
  {  
	 trans[0]=0;
	 for(i=0;mess[i];i++)//扫描mess
     {  
		q=first->next; ///// 
        while(q)  //在编码链表中查找mess[i]
		{ 
		   if(q->data==mess[i])
			   break;//找到mess[i],退出查找
           q=q->next;
		}
        if (q)
			strcat(trans,q->code);//找到mess[i]
	    else //未找到mess[i]
		   fprintf(stderr,"原文包含未编码字符%c,程序将自动跳过\n",mess[i]);
     }
	 trans[strlen(trans)]=0;// end with '\0'
	 if(i==MAX_BUFFER-1)
		 fprintf(stderr,"原文太长,可能已经溢出!\n");
	 fprintf(stderr,"加密后的文字如下:\n");
  }

   else //解密
   {  
	  letter *p=root;//从根节点开始
      int j=-1;
      
	  if(p->lchild)
        for(i=0;mess[i];i++)  //扫描
		   {  
			  if(mess[i]=='0')
		         p=p->lchild; //深入左子树
              else if(mess[i]=='1')
				  p=p->rchild;//深入右子树
              else 
			  {
				 trans[++j]=mess[i];
                 p=root;//重新开始下一次深入查找
				 continue;
              }
			  
			  if(!p->lchild) //遇到叶子节点
			  {  
				 trans[++j]=p->data;
                 p=root;//重新开始下一次深入查找
			  }
		   }
	  else     //只有根节点
	      trans[++j]=p->data;

	  trans[++j]=0;
      if((p!=root)&&p->lchild)
		  fprintf(stderr,"\n密文结尾有误!");
	  if(i==MAX_BUFFER-1)
		  fprintf(stderr,"\n密文太长,可能已经溢出!");
      fprintf(stderr,"\n破译后的文字如下:\n");
   }

   fprintf(stdout,"%s",trans);
   fprintf(stderr,"\n");
}


void Operator()
{
    char ch,buffer[MAX_BUFFER],path[MAX_PATH];
    FILE *openfile;
    
    while(1)
    {  
	   fprintf(stderr,"\n解密(键盘输入)请按1, 加密(键盘输入)请按2,");
       fprintf(stderr," 破译文章请按3, 加密文章请按4,退出请按0: ");
	   ch=getchar();
	   getchar();  //    Strange !!!! 

	   switch(ch)
	   {
		  case '1':  
			        fprintf(stderr,"\n请输入代码串:\n");
		  case '2':
			        if(ch=='2')
						fprintf(stderr,"\n请输入字符串:\n");
		            gets(buffer);                    
                    Translate(buffer,ch%2);  //翻译
					break;
		  case '3':  
		  case '4':
			        fprintf(stderr,"\n请输入文件名:\n");
		            gets(path);
                    if((openfile=fopen(path,"r"))==NULL)
					{
                	    fprintf(stderr,"\nCannot open file: %s \n",path);
	                    break;	 
					}
                    while(!feof(openfile))
					{ 
                 	   if(fgets(buffer,MAX_BUFFER,openfile))                   
                          Translate(buffer,ch%2);  //翻译
					}
					fclose(openfile);
                    break;
		  case '0':
			       	fprintf(stderr,"\n===============退出==============\n\n");
			        exit(0);
          default:  
			        fprintf(stderr,"\n输入错误!\n\n");
	   }
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -