课程设计哈夫曼树文件的压缩

 

 

//课程设计哈夫曼树文件的压缩
#include<iostream>
#include<fstream>
#include<string>
#include<iomanip>
#include"stdio.h"
using namespace std;
string remfile[6000];//存放原文件字符的数组;
int remcount=0;//记录元素个数;
float  bitecount=0;//记录二进制码的个数;
/****************************************************************/
struct huffchar{//存放读入字符的类;
 int count;//字符出现的个数;
    char data;//字符;
};
int count=1;//记录huff数组中字符实际出现的个数;
huffchar huff[1000];//类的对象;

 


/****************************************************************/
/*文件读入部分和统计字符出现的频率*/
bool char_judge(char c)//判断字符出现的函数;
{
  for(int i=1;i<=count;i++)
   if(huff[i].data==c){huff[i].count++;return true;}//如果出现过,出现的频数加1;
   return false;

}

void char_add(char c)//添加新出现的字符;
{
 huff[count].data=c;
 huff[count++].count++;//个数增加,
}

 

//文件的读取
void read_file_count()
{
 char c;
 ifstream infile;
 infile.open("huffman.txt");//打开huffman.txt文件;


 if(!infile)//检查文件是否打开。
 {
  cerr<<"不能打开 huffman.txt文件";//输出文件未打开的标志。
  exit(0);
 }


 cout<<"读入的文件中的内容为:"<<endl;
 while((c=infile.get())!=EOF)
 {

  remfile[++remcount]=c;
 if(!char_judge(c))
  char_add(c);
 }
 cout<<endl;
}

/******************文件读入和统计字符出现频率部分结束**************/

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

/******************构造huffman树程序部分***************************/
struct huff_tree{//huffman树结点定义;
 int parent;
 int lchild;
 int rchild;
 int weight;

};

 int sum;//huffman树中结点的个数;
 huff_tree huffman[1000];
void creathuffman()//构造huffman树的函数
{

 int min1,min2;//指示权值最小;
 int loc1,loc2;//指向权值最小的两个数的位置;


 for(int i=1;i<=sum;i++)
 {   //对huffman树的结点进行初始化;
  huffman[i].parent=0;
  huffman[i].lchild=0;
  huffman[i].rchild=0;
  huffman[i].weight=0;
 
 
 }
 for(int ii=1;ii<count;ii++)//将权值赋给huffman[].weight;
 huffman[ii].weight=huff[ii].count;
 sum=2*count-3;

 
for(int j=count;j<=sum;j++)
{
 loc1=loc2=0;//权值最小的
    min1=min2=20000;
    for(int k=1;k<=j-1;k++)//求权值最小的两个地址;
  if(huffman[k].parent==0)
   if(huffman[k].weight<=min1)
   {
    min2=min1;min1=huffman[k].weight;
                loc2=loc1;loc1=k;
   }
   else
    if(huffman[k].weight<=min2)
    {min2=huffman[k].weight;loc2=k;}
将求出的两个权值最小的结点合并为新的结点,并将新的结点存入数组中
         huffman[loc1].parent=j;
   huffman[loc2].parent=j;
   huffman[j].lchild=loc1;
   huffman[j].rchild=loc2;
   huffman[j].weight=huffman[loc1].weight+huffman[loc2].weight;
  
  }

 
}
 
/*******************************构造huffman树的程序部分结束********************************/

/*************************************huffman编码开始**************************************/ 

struct huffcode{//译码结构体
 string bits[100];//存放解码;
 int start;//
 int count;
 string c;//存放字符;
};
huffcode hcode[100];
void huffmancode()//编码函数
{
 
 int rem,p;int count1=0;
    for(int y=1;y<=count;y++)
 {//编码部分;
  rem=y;
  hcode[y].start=sum;
        hcode[y].c=huff[y].data;
  p=huffman[y].parent;
  while(p!=0)
  {
   if(huffman[p].lchild==rem)hcode[y].bits[++count1]='0'; 
   else   hcode[y].bits[++count1]='1';
  rem=p;
   p=huffman[p].parent;
  }
  hcode[y].count=count1;
  count1=0;
 }

for(int t=1;t<=count;t++)//输出所编的码;
{
 cout<<"字符"<<hcode[t].c<<";编码:  ";
 int r=hcode[t].count;
while(r)
cout<<hcode[t].bits[r--];
cout<<endl;
}

}


/************************************************************************************/
 string str;
void code_huffman_file()
{
    ofstream fp;

 cout<<"请输入文件名"<<endl<<"例如:huffman1.txt"<<endl;
    cout<<"该文件用来存放编码后的文件即压缩文件"<<endl;
 cin>>str;
 
 fp.open(str.c_str());
 if(!fp)//检查文件是否打开。
 {
  cerr<<"不能打开 "<<str<<"文件"<<endl;//输出文件未打开的标志。
  exit(0);
 }
 for(int j=1;j<=remcount;j++)
 {
  for(int i=1;i<=count;i++)
          if(remfile[j]==hcode[i].c)
    {
     for(int k=hcode[i].count;k>0;k--)
     {fp<<hcode[i].bits[k];bitecount++;}
              break;
    }
 }

fp.close();
}

/****************************编码并将编码存入文件部分结束*************************/

 

/
   void code_file_out()//将编码过的文件恢复;
   {
 ifstream fp1;//编码文件;
    ofstream fp2;//解压缩文件;
 fp1.open(str.c_str());
    if(!fp1)//检查文件是否打开。
 {
  cerr<<"不能打开 "<<str<<"文件"<<endl;//输出文件未打开的标志。
  exit(0);
 }

   char inchar;
 cout<<"请输入文件名"<<endl<<"例如:huffman2.txt"<<endl;
    cout<<"该文件存放解压缩后的文件"<<endl;
 string s1;
 cin>>s1;
 
 fp2.open(s1.c_str());
 if(!fp2)//检查文件是否打开。
 {
  cerr<<"不能打开"<<s1<<"文件"<<endl;//输出文件未打开的标志。
  exit(0);
 }


 
  for(int ptr=sum;!fp1.eof();)//将编码转为字符输入的到文件中;
  {
   
  fp1>>inchar;
  if(inchar=='1')ptr=huffman[ptr].rchild;//查找相应编码对应huffman树中的位置,
  else ptr=huffman[ptr].lchild;
     if(huffman[ptr].lchild==0&&huffman[ptr].lchild==0)//判断是否为叶子结点;
  {fp2<<huff[ptr].data;ptr=sum;}//是叶子结点,将该结点的对应字符输入到文件中;
  }
  cout<<endl<<"                请检查原文件"<<"huffman.txt"<<"与解压缩文件"<<s1<<endl<<endl<<endl;
  cout<<"*********************************请检查*****************************"<<endl;
}
          
/*************************解压缩文件部分结束**************************************/       

void evaluating()
{
 float y1;
 y1=bitecount/8/remcount*100;
 cout<<"压缩比例是:"<<y1<<"%"<<endl;
}

 

 

void main()
{
 cout<<"          *******************************************************"<<endl;
 cout<<"          *                 数据结构课程设计                    *"<<endl;
 cout<<"          *                 Huffman树文件压缩                   *"<<endl;
 cout<<"          *                      ****                         *"<<endl;
 cout<<"          *                  *********                       *"<<endl;
 cout<<"          *******************************************************"<<endl;
 system("pause");
 read_file_count();
 creathuffman();
 huffmancode();
 code_huffman_file();
 code_file_out();
 evaluating();
 cout<<endl<<endl<<"                      文件的压缩与解压缩完成"<<endl;
  system("pause");
}

 

 

关于Huffman 压缩

0.原理
  Huffman编码是一种可变长编码方式,是由美国数学家David Huffman创立的,是二叉树的一种特殊转化形式。编码的原理是:将使用次数多的代码转换成长度较短的代码,而使用次数少的可以使用较长的编码,并且保持编码的唯一可解性。Huffman算法的最根本的原则是:累计的(字符的统计数字*字符的编码长度)为最小,也就是权值(字符的统计数字*字符的编码长度)的和最小。


1.Huffman树

Huffman树是二叉树的一种特殊转化形式。以下是构件Huffman树的例子:
比如有以下数据, ABFACGCAHGBBAACECDFGFAAEABBB
先进行统计A(8) B(6) C(4) D(1) E(2) F(3) G(3) H(1)  括号里面的是统计次数

生成Huffman树:每次取最小的那两个节点(node)合并成一个节点(node),并且将累计数值相加作为新的接点的累计数值,最顶层的是根节点(root)  注:列表中最小节点的是指包括合并了的节点在内的所有节点,已经合并的节点不在列表中


运算的过程如下:
1:D+H(2)
2:DE+H(4)
3:F+G(6)
4:C+DEH(8)
5:B+FG(12)
6:A+CDEH(16)
7:ACDEH+BFG(28)


那么转化为Huffman树就是

        Huffman树            层数

          Root         
        ┌┴┐
      ACDEH  BFG              1
      ┌┴┐┌┴┐
    CDEH A B  FG            2
    ┌┴┐    ┌┴┐
  DEH  C    F    G          3
  ┌┴┐
  DH  E                      4
┌┴┐
D    H                        5

取左面是1 右面是0 则有。
注:层数就是位数或者说是代码长度,权值=代码长度*该字的统计次数。

  代码          位数      权值
  A = 10          2        16
  B = 01          2        12
  C = 110        3        12
  D = 11111      5          5
  E = 1110        4          8
  F = 001        3          9
  G = 000        2          6
  H = 11110      5          5

可以看出Huffman代码是唯一可解的(uniquely decodable),如果你读到110就一定是C ,不会有任何一个编码是以110开始的。

 

如果不使用Huffman算法,而使用普通的编码,结果是什么呢?

        Huffman树            层数

          Root
        ┌┴┐
      ABCD    EFGH            1
  ┌┴┐      ┌┴┐
  AB  CD    EF  GH        2
┌┴┐┌┴┐┌┴┐ ┌┴┐
A  B C  D E  F G  H      3

取左面是1 右面是0 则有

  代码          位数      权值
  A = 111        3        24
  B = 110        3        18
  C = 101        3        12
  D = 100        3          3
  E = 011        3          6
  F = 010        3          9
  G = 001        3          9
  H = 000        3          3

利用Huffman编码得到的权值累计是 73,如果利用普通定长编码的话,则要用84字符长度。从这个比较,你可以看出,Huffman是怎么进行压缩的。


2.编码和解码
编码:将ABCDEFGH用Huffman树产生的编码对应着写到文件中,并且保留原始的Huffman树,主要是编码段的信息。一般要编码256个元素的话需要511个单位来储存Huffman树,每个Huffman树都必须有以下的结构:code,char,left,right,probability(出现次数),通常情况是利用一个数组结构。因为在解码的时候只需要用到code,所以只需要记录每个元素的编码就可以了。

解码:利用文件中保存的Huffman编码,一一对应,解读编码,把可变长编码转换为定长编码。

3.发展
由于Huffman编码需要扫描两次,第一次是统计数字,第二次是编码写文件,大大影响了速度,因此有人发明了enhanced Huffman aglorithm。这种算法只扫描一遍文件,动态产生Huffman树,即每读n个字节就重新编码一次Huffman树,以达到提高速度的目的。在解码的过程中使用动态还原技术。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值