码迷,mamicode.com
首页 > 编程语言 > 详细

数据结构与算法分析综合实验:用哈夫曼编码实现文件压缩

时间:2021-01-27 13:40:41      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:roman   loading   删除   parent   time   构造   退出   树根   sizeof   

一、实验目的

1、了解文件的概念。

2、掌握线性链表的插入、删除等算法。

3、掌握Huffman树的概念及构造方法。

4、掌握二叉树的存储结构及遍历算法。

5、利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。

 

二、设备与环境

微型计算机、Windows系列操作系统 、Visual C++6.0软件等。

 

三、实验内容

根据ASCII码文件中各ASCII字符出现的频率情况创建Huffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。

 

四、实验结果及分析

1、概要设计

(1)构造哈夫曼树的哈夫曼算法

  构造哈夫曼树步骤:

  <1>根据给定的n个权值{w1,w2……wn},构造n棵树只有根结点的二叉树,起权值为wj。

       <2>在森林中选取两棵根结点权值最小和次小的树作为左右子树,构造一棵新的二叉树,置新的二叉树根结点权值为其左右子树根结点权值之和。

  <3>在森林中删除这两棵树,同时将新得到的二叉树加入森林中。

  重复上述两步,直到只含一棵树为止,这棵树即为哈夫曼树。

  算法结构如图:

      技术图片

(2)哈夫曼编码:数据通信用的二进制编码

  思想:根据字符出现的频率编码,使电文总长最短

  编码:根据字符出现的频率构造哈夫曼树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列。

(3)文本编码

  读取存放在文本中的字母,一对一的进行编译,将对应的编码存放在另一个文本中。

2、详细设计

1压缩过程图解

    技术图片

(2)详细代码

 

  1 #include <stdio.h> 
  2 #include <string.h> 
  3 #include <stdlib.h> 
  4 #include <conio.h> 
  5 
  6 /*哈夫曼树结构定义*/
  7 struct head 
  8 { 
  9     unsigned char b;            /*定义一个字符*/
 10     long count;                 /*频率数据*/ 
 11     long parent,lch,rch;        /*创建哈夫曼树*/ 
 12     char bits[256];             /*哈夫曼结点*/ 
 13 }header[512],tmp; 
 14 
 15 
 16 /*压缩文件*/
 17 void yasuo()                                 
 18 { 
 19     char filename[255],outputfile[255],buf[512]; 
 20     unsigned char c; 
 21     char wenjianming[255];
 22     long i,j,m,n,f; 
 23     long min1,pt1,flength;
 24     FILE *ifp,*ofp; 
 25     printf("请输入文件地址及文件名:"); 
 26     gets(filename); 
 27     ifp = fopen(filename,"rb");     /*打开源文件并读取*/
 28     while(ifp==NULL)
 29     { 
 30         printf("打开文件时出错!\n"); 
 31         printf("请重新输入文件地址及文件名:"); 
 32         gets(filename); 
 33         ifp=fopen(filename,"rb");          
 34     }
 35     printf("请输入压缩后的文件地址和文件名及后缀:");
 36     gets(wenjianming);
 37     ofp = fopen(wenjianming,"wb");    /*创建并打开目的文件*/
 38     
 39     while(ofp==NULL)
 40     {
 41         printf("请重新输入压缩后的文件地址和文件名及后缀:");
 42         ofp=fopen(wenjianming,"wb");          
 43     }
 44     flength = 0; 
 45     
 46     /*读取ifp文件*/
 47     while(!feof(ifp))               
 48     { 
 49         fread(&c,1,1,ifp);           /*按位读取文件*/
 50         header[c].count++;           /*记录文件的字符总数*/
 51         flength++; 
 52     } 
 53     flength = -1;                   
 54     header[c].count = -1;             /*读取文件结束*/
 55     /*构造哈弗曼树,初始结点的设置*/
 56     for(i=0;i<512;i++)                
 57     { 
 58         if(header[i].count != 0) 
 59             header[i].b = (unsigned char)i; 
 60         else 
 61             header[i].b = 0; 
 62         header[i].parent = -1; 
 63         header[i].lch = header[i].rch = -1; 
 64     } 
 65     /*按结点出现的次数排序*/
 66     for(i=0;i<256;i++)         
 67     { 
 68         for(j=i+1;j<256;j++) 
 69         { 
 70             if(header[i].count < header[j].count) 
 71             { 
 72                 tmp=header[i]; 
 73                 header[i] = header[j]; 
 74                 header[j] = tmp; 
 75             } 
 76         } 
 77     } 
 78     /*统计不同字符的数量*/
 79     for(i=0;i<256;i++) 
 80         if(header[i].count==0) 
 81             break; 
 82     n=i; 
 83     m=2*n-1; 
 84     for(i=n;i<m;i++) 
 85     { 
 86         min1=999999999; 
 87         for(j=0;j<i;j++) 
 88         { 
 89             if(header[j].parent!=-1) 
 90                 continue; 
 91             if(min1>header[j].count) 
 92             { 
 93                 pt1=j; 
 94                 min1=header[j].count; 
 95                 continue; 
 96             } 
 97         } 
 98         header[i].count=header[pt1].count; 
 99         header[pt1].parent=i; 
100         header[i].lch=pt1; 
101         min1=999999999; 
102         for(j=0;j<i;j++) 
103         { 
104             if(header[j].parent!=-1)
105                 continue; 
106             if(min1>header[j].count) 
107             { 
108                 pt1=j; 
109                 min1=header[j].count; 
110                 continue; 
111             } 
112         } 
113         header[i].count+=header[pt1].count; 
114         header[i].rch=pt1; 
115         header[pt1].parent=i; 
116     }
117     /*构造哈夫曼树,设置字符编码*/
118     for(i=0;i<n;i++) 
119     { 
120         f = i; 
121         header[i].bits[0] = 0; 
122         while(header[f].parent != -1) 
123         { 
124             j = f; 
125             f = header[f].parent; 
126             if(header[f].lch==j) 
127             { 
128                 j = strlen(header[i].bits); 
129                 memmove(header[i].bits+1,header[i].bits,j+1); 
130                 header[i].bits[0]=0; 
131             } 
132             else 
133             { 
134                 j=strlen(header[i].bits); 
135                 memmove(header[i].bits+1,header[i].bits,j+1); 
136                 header[i].bits[0]=1; 
137             } 
138         } 
139     } /*哈弗曼构造结束*/
140     
141     //读取源文件中的每一个字符,按照设置好的编码替换文件中的字符
142     fseek(ifp,0,SEEK_SET);                    /*把文件指针指向文件的开头*/
143     fwrite(&flength,sizeof(int),1,ofp);     /*把哈弗曼代码写入ofp文件*/
144     fseek(ofp,8,SEEK_SET);                   /*以8位二进制数为单位读取*/
145     buf[0] = 0; 
146     f = 0; 
147     pt1 = 8; 
148     while(!feof(ifp)) 
149     { 
150         c=fgetc(ifp);        //从流中读取一个字符,并增加文件指针的位置
151         f++; 
152         for(i=0;i<n;i++) 
153         { 
154             if(c==header[i].b) 
155                 break; 
156         } 
157         strcat(buf,header[i].bits); //把header[i].bits所指字符串添加到buf结尾处
158         j = strlen(buf);            //计算字符串buf的长度
159         c = 0; 
160         while(j>=8)     /*按八位二进制数转化成十进制ASCII码写入文件一次进行压缩*/
161         { 
162             for(i=0;i<8;i++)  
163             { 
164                 if(buf[i]==1) c=(c<<1)|1; 
165                 else c=c<<1; 
166             } 
167             fwrite(&c,1,1,ofp); 
168             pt1++; 
169             strcpy(buf,buf+8); 
170             j=strlen(buf); 
171         } 
172         if(f==flength) 
173             break; 
174     } 
175     if(j > 0) /*剩余字符数量少于8个*/
176     { 
177         strcat(buf,"00000000"); 
178         for(i=0;i<8;i++) 
179         { 
180             if(buf[i]==1) c=(c<<1)|1; 
181             else c = c << 1;     /*对不足的位数补0*/
182         } 
183         fwrite(&c,1,1,ofp); 
184         pt1++; 
185     } 
186     //将编码信息写入存储文件
187     fseek(ofp,4,SEEK_SET);     /*fseek 用于二进制方式打开的文件,移动文件读写指针位置.第一个是文件流,第3个是指针零点位置,第2个是把指针移动到的地点. */
188         fwrite(&pt1,sizeof(long),1,ofp); /*是要输出数据的地址,每次写入的位数,数据项的个数,目标文件地址*/
189     fseek(ofp,pt1,SEEK_SET); 
190     fwrite(&n,sizeof(long),1,ofp); 
191     for(i=0;i<n;i++) 
192     { 
193         fwrite(&(header[i].b),1,1,ofp); 
194         c=strlen(header[i].bits); 
195         fwrite(&c,1,1,ofp); 
196         j=strlen(header[i].bits); 
197         if(j % 8!=0)             /*按八位读取,位数不满8位时,对该位补0*/    
198         { 
199             for(f=j%8;f<8;f++) 
200                 strcat(header[i].bits,"0"); 
201         } 
202         while(header[i].bits[0]!=0) 
203         { 
204             c=0;             
205             for(j=0;j<8;j++) 
206             { 
207                 if(header[i].bits[j]==1) c=(c<<1)|1; 
208                 else c = c << 1; 
209             } 
210             strcpy(header[i].bits,header[i].bits+8);  /*把从header[i].bits+8地址开始且含有NULL结束符的字符串赋值到以header[i].bits开始的地址空间 */
211             fwrite(&c,1,1,ofp); 
212         } 
213     } 
214     fclose(ifp); 
215     fclose(ofp); 
216     printf("压缩成功\n"); 
217    
218 } 
219 /*主函数*/
220 void main()                   
221 {
222     printf("输入a开始压缩\n");
223     printf("输入b结束压缩\n");
224     while(1)
225     {
226         char c;
227         c=getch();
228         if(c==a)
229             yasuo();
230         else
231         {
232             if(c==b)
233             return;
234         }
235     }
236 }

 

3、测试结果及分析

(1)运行截图如下

  键入a,输入文件地址及文件名和压缩后的文件地址及文件名,在出现“压缩成功”后键入b,退出代码运行。

       技术图片

 

2压缩结果展示

  a.压缩前(只有一个文件)

        技术图片

  打开后文件内容

   技术图片

  b.压缩后(出现两个文件)

   技术图片

  打开后文件内容

  技术图片

 

 

五、实验总结

 

本学期要学习课程很多,留给综合实验的时间并不是特别多。通过本次的实验,我又重新拿出了上学期的《C语言程序设计》,重新复习了一下一些关于文件读取、写入等函数。对于本次的实验内容,不仅仅是对本学期《数据结构与算法分析》的一个学习总结,更是对上学期C语言的复习回顾。

 

本次实验代码调试了很久,还是会出现一些C语言语法上的小错误,还有一部分内容参考了网上的代码。除了技术上的错误,在本次实验中我觉得还是要保持一个良好的心态去解决错误,在我的代码出现多次报错时,要能沉得住气去纠错,忌浮躁。

 

总之要学习的东西还有很多,每完成一次实验对我自己的能力来说都是一个很好的提升过程。作为大数据专业的学生,数学虽说是基础,但能够更好解决实际问题还是要靠计算机程序代码,学会一门语言不仅仅限于书本,更是要能够熟练地在实际中应用。学习是一个逐渐进步的过程,每次实验内容不仅仅是一次实验,更是一次对自己知识掌握的检验,更是对自己在面对新的挑战前的磨练。

 

 

 

 

 

数据结构与算法分析综合实验:用哈夫曼编码实现文件压缩

标签:roman   loading   删除   parent   time   构造   退出   树根   sizeof   

原文地址:https://www.cnblogs.com/SLXii/p/14331487.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!