Huffman编解码实现文本压缩

 
编码方案设计:
实现最基本的哈夫曼编码,对文件进行两次扫描,第一次统计概率,第二次进行编码。压缩和解压缩时分别重新建立树,重新编码,以减小压缩后文件大小。
系统实现方案:
 
typedef  struct  HT
{
        
char ch;
        unsigned 
long weight;
        
struct HT *pre;
        
struct HT *next;
        
struct HT *parent;
        
struct HT *LChild;
        
struct HT *RChild;
}
HuffmanTree,RateTabNode; // 树结点,同时也是双链表结点
 
统计概率(权) :对文本进行扫描,统计字符种类和相应的权(数量),将结果保存在由以上结构构成的双链表中,返回头指针。
相关函数:
RateTabNode  * Probability(FILE  * ifp) // 对字符作分类并统计数量(权)
{
        
char temp;
        RateTabNode 
*head,*p,*q;
        
        q
=(RateTabNode *)malloc(sizeof(RateTabNode));
        head
=p=q;
        q
->next=NULL;
        head
->pre=NULL;
        head
->LChild=head->RChild=NULL;
        q
->ch=temp=fgetc(ifp);
        q
->weight=0;
        
        
while(!feof(ifp))
        
{
            
            
if(!IsExisted(head,temp))//如果存在temp字符,则权加(IsExisted函数中);不存在,创建新结点
            {
                q
=(RateTabNode *)malloc(sizeof(RateTabNode));
                p
->next=q;
                q
->pre=p;
                p
=q;
                p
->next=NULL;
                p
->LChild=p->RChild=NULL;
                
                p
->ch=temp;
                p
->weight=1;
            }

            temp
=fgetc(ifp);
        }

        
return head;
}

int  IsExisted(RateTabNode  * head, char  ch) // 判断当前双链表中是否有ch字符,若有则数量加并返回真,否则返回假
{
        RateTabNode 
*temp=head;
        
while(temp!=NULL)
        
{
            
if(ch==temp->ch)
            
{
                
++temp->weight;
                
return 1;
            }

            temp
=temp->next;
        }

        
return 0;
}

 
递归建立Huffman树:
1. 搜索双链表中权值最小和次小的结点(p1,p2),并将它们从双链表中移除,新建另一个结点(root),将p1和p2做为叶子链接到root结点,并把root结点添加到双链表,root的权为p1和p2权的和。
2. 递归以上过程,直到双链表中剩下两个结点。
3. 当剩下两个结点时,新建结点Root,两个结点分别为左右儿子,返回Root指针。
相关函数:

HuffmanTree *HTCreate(RateTabNode *head)//双链表形式创建Huffman树
{
 HuffmanTree *p1,*p2,*root;
    RateTabNode *temp,*p;

 root=(HuffmanTree *)malloc(sizeof(HuffmanTree));
 //printf("%d/n",SizeAssure(head));
    if(SizeAssure(head)==2)//当只生剩下两个元素,直接将这两棵树作为子树,返回总根结点地址
 {
  root->ch='/0';
  root->LChild=head;
  head->parent=root;
  root->RChild=head->next;
  root->weight=head->weight+head->next->weight;
  head->next->parent=root;
  head=root;
        head->parent=NULL;

  return head;
 }
 else
 {
   for(p=temp=head;p!=NULL;p=p->next)//找权最小的结点
   {
  if(temp->weight>p->weight)
   temp=p;
  
   }
   p1=temp;
   /****记下最小结点的地址,将它从链表中移除(分三种情况,头、尾和中间)***/
   if(p1->pre==NULL)//表头
   {
    head=head->next;
    head->pre=NULL;
   }
   else if(p1->next==NULL)//表尾
   {
    p1->pre->next=NULL;
   }
   else
   {
       p1->pre->next=p1->next;
    p1->next->pre=p1->pre;
   
   }//中间   
   p1->next=p1->pre=NULL;
   /*********寻找次小权结点,过程同上面完全相同,找去掉最小后的最小,即次小**********/
   for(temp=p=head;p!=NULL;p=p->next)
   {
  if(temp->weight>p->weight)
   temp=p;
   }
   p2=temp;
   if(p2->pre==NULL)
   {
    head=head->next;
    head->pre=NULL;
   }
   else if(p2->next==NULL)
   {
    p2->pre->next=NULL;
   }
   else
   {
       p2->pre->next=p2->next;
    p2->next->pre=p2->pre;
   
   }
   //取次小值
      p2->next=p2->pre=NULL;
   //printf("%d/n",SizeAssure(head));getch();
   /*********将两个最小值建立一棵子树,并将它们的根结点插入双链表**********/
   root->LChild=p1;
   root->RChild=p2;
   p1->parent=root;
   p2->parent=root;
    
   root->ch='/0';
   root->weight=p1->weight+p2->weight;
   root->next=head;
   head->pre=root;
   head=root;
   head->pre=NULL;

   head=HTCreate(head);//递归,直到剩两个结点在双链表中
 }

编码:
采用自下向上的编码方式,从叶子开始,若当前结点是父结点的左儿子则在码序列末尾写 0 ,否则写 1 ,然后结点向上移动,直到结点的父结点 (parent) 为空即根结点结束。
相关函数:
void FindLeaf(RateTabNode *head,LeafNode *leafhead,int size)//递归查找叶子,记下地址到数组
{
 static int num=0;
 if(head->LChild==NULL&&head->RChild==NULL&&num<size)
 {
  leafhead[num].ch=head->ch;
  leafhead[num].leaf=head;
  ++num;
  return;
 }
 if(head==NULL)return;
 
 FindLeaf(head->LChild,leafhead,size);
 FindLeaf(head->RChild,leafhead,size);
 
}
void HFCoding(LeafNode *leafhead,CodeNode *codehead,int size)//自下向上编码,左0右1
{
     int i=0,j=CodeMaxLen;
  LeafNode *leaftmp=leafhead;
  while(i<size)
  {
   j=CodeMaxLen;
   codehead[i].ch=leaftmp[i].ch;
   codehead[i].code[--j]='/0';
      while(leaftmp[i].leaf->parent!=NULL&&j>=0)
      {
    if(leaftmp[i].leaf->parent->LChild==leaftmp[i].leaf)
     codehead[i].code[--j]='0';
    if(leaftmp[i].leaf->parent->RChild==leaftmp[i].leaf)
     codehead[i].code[--j]='1';
    leaftmp[i].leaf=leaftmp[i].leaf->parent;//向上
      }
   ++i;
  }
系统演示结果:
纯英文文档,压缩到原来的一半左右;中英文混合,效果差一些;纯中文效果不太好。
 
系统性能分析:
 递归建立 Huffman 树形结构,以及递归遍历树结点以寻找叶子,递归算法耗费系统资源较大,复杂度较高,但是现在的系统硬件条件优越,处理还是很迅速,基本不是问题。由于压缩时需要保留权信息,当原文件较小时,可能会存在“压大”的情况。
 系统总体性能优越,完美实现压缩和解压缩。
  改进:
  在建立 Huffman 树的同时完成编码,而不要再重新遍历自底向下的大费周折; Huffman 压缩之后将产生大量的0和1,可以再用游程编码进行压缩,效果应该更好。
完整代码(gcc or C++ compiler):

/********************************************/

/*         Huffman编码的无损压缩V2.0       */

/*              2008.3.28                   */

/*            作者:付闯                 */

/********************************************/

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

#include<malloc.h>

#include<string.h>

#define MaxSize 65536

//定义缓冲区最大高度

#define CodeMaxLen 32

//定义Huffman编码最大长度

typedef struct HT

{

        char ch;

         unsigned long weight;

         struct HT *pre;

         struct HT *next;

         struct HT *parent;

         struct HT *LChild;

         struct HT *RChild;

}HuffmanTree,RateTabNode;//树结点,同时也是双链表结点

typedef struct

{

     char ch;//字符

     char code[CodeMaxLen];//字符的编码

}CodeNode;//存储字符的Huffman编码

typedef struct

{

     char ch;

     HuffmanTree *leaf;

}LeafNode;//临时存储叶子结点的地址

typedef struct

{

     char ch;

     unsigned long weight;

}OFinal;//最后写入文件的单元

 

void compress(FILE *,FILE*);//压缩

void decompress(FILE *,FILE*);//解压缩

/*********************统计字符权并编码************************/

RateTabNode *Probability(FILE *);//建立双链表

int IsExisted(RateTabNode *,char);//统计文本中字符个数

int SizeAssure(RateTabNode *);//返回双链表大小,即字符的总个数

HuffmanTree* HTCreate(RateTabNode *);//创建Huffman

void FindLeaf(RateTabNode *,LeafNode *,int);//找出所有叶子并记录地址

void HFCoding(LeafNode *,CodeNode *,int);//从叶子到根,从下向上的进行编码左0 右1

void buf_init(char (*)[8],int);//缓冲初始化

void wbuffer(FILE *,CodeNode *,char (*)[8],int,int);//

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值