一、实验目的

1.目的:掌握赫夫曼(Huffman)树和赫夫曼编码的基本思想和应用。
2.任务:实现文件中数据的加解密与压缩。

二、实验内容及要求

1、任务描述
实验内容:将硬盘上的一个文本文件进行加密,比较加密文件和原始文件的大小差别;
对加密文件进行解密,比较原始文件和解码文件的内容是否一致。
输入和输出:
(1)输入:硬盘上给定的原始文件及文件路径。
(2)输出:
◆ 硬盘上的加密文件及文件路径;
◆ 硬盘上的解码文件及文件路径;
◆ 原始文件和解码文件的比对结果。
实验要求:
◆ 提取原始文件中的数据(包括中文、英文或其他字符),根据数据出现的频率为权
重,构建 Huffman 编码表;
◆ 根据 Huffman 编码表对原始文件进行加密,得到加密文件并保存到硬盘上;
◆ 将加密文件进行解密,得到解码文件并保存点硬盘上;
◆ 比对原始文件和解码文件的一致性,得出是否一致的结论。
2、主要数据类型与变量
通过从头遍历链表中的元素并依次与待插入的结点的权值进行比较,从而确定待插入结点的位置,使之从后形成的链表中的元素都按权值从大到小排列。

Find_and_insert函数:

将待插入结点的high值和low值依次与head链表中的元素进行比较,如果与之相同则代表是相同的字符。将该结点的权值加一,然后弹出链表重新比较权值的大小再将其插入。

Output函数:

遍历链表中的每一个元素,并将信息输出。

tree_node_init函数:

初始化树的每一个结点。

build_tree函数:

通过遍历链表将其每一个元素依次插入到树中。

print_huffman_pre函数:

按照参数flag的值对树进行遍历输出。

update_tree函数:

通过层级遍历的顺序进行编码,如果从该节点向左,则左孩子的编码为该节点编码 + '0’;如果从该节点向右,则右孩子的编码为该节点编码 + '1’;

Coding函数:

从已有的树的结点中依次取出结点,通过insert算法小将其按照权值的大小插入到树中,直到权值为1,则最后的这颗树就是所求的哈夫曼树。

Decoding函数:

对文件进行解码,对比输入输出文件的名称,然后以读、写方式打开文件对字符进行对比,统计中文字符和非中文字符。

三、测试

1、方案
创建测试记事本,输入文字并以ANSI的编码格式保存。

2、结果
1)统计信息:

2)词频统计

3)编码详细

4)文件输出信息

编码与解码文件:

四、总结与讨论

本次实验涉及到了很多方面的知识点,包括编码格式Ascii或GBK的选择、文件的读写、赫夫曼树的建立、赫夫曼编码及解码等,通过本次实验,我进一步掌握了赫夫曼树的关键-最优二叉树,即带权路径长度最小的二叉树,而赫夫曼编码使用的编码表是根据每一个字符出现的估算概率而建立起来的。赫夫曼编码可以将输入字符串编译成二进制代码,输入二进制代码也可以将其解码为字符串。
同时我也认识到根据不同的需求,采用不同的数据存储方式,不一定要用栈、二叉树等等高级类型,有时用基本的一维数组,只要运用得当也能达到相同的效果甚至更佳。
总之本次实验确实很有难度,虽然书上有关赫夫曼树的每一个知识点我都看了并且代码也基本都理解,但是实际利用赫夫曼树的思想进行上机实验还是感觉有些吃力,尤其是赫夫曼最小路径的存取,赫夫曼编码及译码的应用范围等一系列问题使我对数据结构更加树立起了学习之心,今后还要及时发现自己平时学习的不足和薄弱环节,从而加以弥补。

附:程序的源代码

#include <stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct node* LIST;
typedef struct node* TREE;
struct node
{int high;//存储Ascii码的所有字符int low;//协助存储一个汉字int weight;//统计字符个数int code_index;//编码指针char code[100];//编码数组TREE Left;//哈夫曼树左孩子TREE Right;//哈夫曼树右孩子LIST NEXT;//下一个结点
};
void insert(LIST head, LIST tmp);//有序插入结点
LIST find_and_insert(LIST head, LIST tmp);//弹出内部的结点,然后调用insert函数
void output(LIST head);//输出这个读取文件的所有字符统计情况
LIST init_LIST(int high, int low, int weight);//初始化链表信息
TREE tree_node_init(int high, int low, int weight);//初始化哈夫曼树各个结点
TREE build_tree(LIST head);//建立哈夫曼树
void print_huffman_pre(TREE Tree, int flag);//前序输出,flag 控制是否打印信息
void update_tree(TREE Tree);//更新树的信息,也即更新编码信息
void save_file(TREE* a, int right, TREE Tree);//保存文件
void to_free_tree(TREE Tree);//释放树
void to_free_list(LIST head);//释放链表
void coding();//编码
void decoding();//解码
TREE queue[1000];//树
int queue_index = 0;//索引
int sum_bit_coding = 0;//编码bit
int sum_bit_decoding = 0;//解码bit
char file_in[100] = "";//文件默认打开路径
char file_out[100];
void init_main();
void update_file_out(char file_in[])
{int i;for (i = strlen(file_in) - 1; i >= 0; i--)if (file_in[i] == '\\')break;int j;for (j = 0; j <= i; j++)file_out[j] = file_in[j];
}
int main()
{init_main();return 0;
}
void init_main()
{LIST head = init_LIST(0, 0, 0);FILE* P;while (1){printf("请输入文件的路径\n");gets(file_in);P = fopen(file_in, "r");if (P == NULL){printf("文件打开失败\n请重新输入\n");continue;}else{printf("打开成功,请选择操作命令:\n");break;}}update_file_out(file_in);int ch;while ((ch = fgetc(P)) != EOF){LIST tmp = init_LIST(ch, -1, 1);if (ch > 128)tmp->low = fgetc(P);insert(head, find_and_insert(head, tmp));}//output(head);TREE Tree = build_tree(head);coding(Tree);print_huffman_pre(Tree->NEXT, 0);update_tree(Tree);queue_index = 0;print_huffman_pre(Tree->NEXT, 0);decoding();fclose(P);while (1){int flag;printf("---------------操作命令集---------------\n");printf("****************************************\n");printf("*            1.统计信息                 *\n");printf("*            2.词频统计                 *\n");printf("*            3.编码详细                 *\n");printf("*            4.文件输出信息             *\n");printf("*            5.Return                   *\n");printf("****************************************\n");scanf("%d", &flag);switch (flag){case 1:printf("-----------------------------------\n");printf("文件已经保存,共写入%d比特\n", sum_bit_coding);printf("从文件读取%d比特\n", 8 * (Tree->high + Tree->low * 2));printf("压缩率是%.3f\n", 1.0 * sum_bit_coding / 8 / (Tree->high + Tree->low * 2));printf("共写入解码文件%d比特\n", sum_bit_decoding);printf("-----------------------------------\n");break;case 2:output(head);break;case 3:print_huffman_pre(Tree->NEXT, 1);break;case 4:{//路径codingchar coding_file_name[100];strcpy(coding_file_name, file_out);strcat(coding_file_name, "coding.txt");//路径encodingchar encoding_file_name[100];strcpy(encoding_file_name, file_out);strcat(encoding_file_name, "encoding.txt");printf("输入文件的路径为%s\n", file_in);printf("加密文件的路径为%s\n", coding_file_name);printf("解码文件的路径为%s\n", encoding_file_name);break;}case 5:return;}}to_free_tree(Tree);to_free_list(head);
}
void decoding()
{sum_bit_decoding = 0;//路径codingchar coding_file_name[100];strcpy(coding_file_name, file_out);strcat(coding_file_name, "coding.txt");//路径encodingchar encoding_file_name[100];strcpy(encoding_file_name, file_out);strcat(encoding_file_name, "encoding.txt");FILE* in = fopen(coding_file_name, "r");FILE* out = fopen(encoding_file_name, "wb");int ch;int str_index = 0, left;char str[100];while ((ch = fgetc(in)) != EOF){str[str_index++] = ch;str[str_index] = '\0';for (left = 0; left < queue_index; left++){if (strcmp(queue[left]->code, str) == 0){//bitsif (queue[left]->high > 128) sum_bit_decoding += 16;else sum_bit_decoding += 8;if ((char)queue[left]->high == '\n'){fprintf(out, "\r\n");}else{fprintf(out, "%c", queue[left]->high);if (queue[left]->high > 128) fprintf(out, "%c", queue[left]->low);}str_index = 0;break;}}}fclose(in);fclose(out);
}
void to_free_list(LIST head)
{LIST P = head;while (head->NEXT){P = head->NEXT;head->NEXT = head->NEXT->NEXT;free(P);}free(head);
}
void to_free_tree(TREE Tree)
{if (!Tree) return;to_free_tree(Tree->Left);to_free_tree(Tree->Right);free(Tree);
}
void save_file(TREE* a, int right, TREE Tree)
{int left;sum_bit_coding = 0;FILE* P = fopen(file_in, "r");//路径char coding_file_name[100];strcpy(coding_file_name, file_out);strcat(coding_file_name, "coding.txt");FILE* out = fopen(coding_file_name, "wb");if (P == NULL)printf("文件打开失败\n");int ch;while ((ch = fgetc(P)) != EOF){LIST tmp = init_LIST(ch, -1, 1);if (ch > 128)tmp->low = fgetc(P);// 查找for (left = 0; left < right; left++){if (a[left]->high == tmp->high){if (tmp->high > 128 && tmp->low == a[left]->low){fprintf(out, "%s", a[left]->code);sum_bit_coding += strlen(a[left]->code);}if (tmp->high <= 128){fprintf(out, "%s", a[left]->code);sum_bit_coding += strlen(a[left]->code);}}}free(tmp);}fclose(P);fclose(out);
}
void update_tree(TREE Tree)
{TREE a[1000];int left = 0, right = 0;if (!Tree) return;a[right++] = Tree->NEXT;while (left < right){//左if (a[left]->Left){a[right++] = a[left]->Left;strcpy(a[left]->Left->code, a[left]->code);a[left]->Left->code_index = strlen(a[left]->code);a[left]->Left->code[a[left]->Left->code_index++] = '0';}//右if (a[left]->Right){a[right++] = a[left]->Right;strcpy(a[left]->Right->code, a[left]->code);a[left]->Right->code_index = strlen(a[left]->code);a[left]->Right->code[a[left]->Right->code_index++] = '1';}left++;}save_file(a, right, Tree);
}
TREE tree_node_init(int high, int low, int weight)
{TREE tmp = (TREE)malloc(sizeof(struct node));tmp->high = high;tmp->low = low;tmp->weight = weight;strcpy(tmp->code, "\0");tmp->code_index = 0;tmp->Right = NULL;tmp->Left = NULL;tmp->NEXT = NULL;return tmp;
}
TREE build_tree(LIST head)
{TREE Tree = tree_node_init(head->high, head->low, 0);TREE T = Tree;LIST P = head->NEXT;while (P){T->NEXT = tree_node_init(P->high, P->low, P->weight);T = T->NEXT;//结点数Tree->weight++;P = P->NEXT;}return Tree;
}
void coding(TREE Tree)
{while (Tree->weight > 1){TREE t1 = Tree->NEXT;Tree->NEXT = Tree->NEXT->NEXT;TREE t2 = Tree->NEXT;Tree->NEXT = Tree->NEXT->NEXT;//add t1 and t2 to t; 合并树TREE t = tree_node_init(-1, -1, t1->weight + t2->weight);//左树t->Left = t1;//右树t->Right = t2;insert(Tree, t);Tree->weight--;}
}
void print_huffman_pre(TREE Tree, int flag)
{//遍历if (!Tree) return;if ((char)Tree->high == '\n'){queue[queue_index++] = Tree;if (flag)printf("\\n   weight == %d coding = %s\n", Tree->weight, Tree->code);}else if (Tree->high > 128){queue[queue_index++] = Tree;if (flag){putchar(Tree->high);putchar(Tree->low);printf("   weight == %d coding = %s\n", Tree->weight, Tree->code);}}else if (Tree->high != -1){queue[queue_index++] = Tree;if (flag){putchar(Tree->high);printf("   weight == %d coding = %s\n", Tree->weight, Tree->code);}}print_huffman_pre(Tree->Left, flag);print_huffman_pre(Tree->Right, flag);
}
LIST find_and_insert(LIST head, LIST tmp)
{//统计汉字和其它字符的不重复个数if (tmp->low != -1) head->low++;//tem_>low不为1代表其为中文字符else head->high++;LIST P = head;while (P->NEXT){//这个字符相同if (P->NEXT->high == tmp->high){//汉字相同情况if (P->NEXT->low != -1 && tmp->low != -1 && P->NEXT->low == tmp->low){//取出当前结点LIST found = init_LIST(P->NEXT->high, P->NEXT->low, P->NEXT->weight + 1);//删除LIST del = P->NEXT;P->NEXT = P->NEXT->NEXT;del->NEXT = NULL;free(del);return found;}if (P->NEXT->low == -1 && tmp->low == -1){//取出当前结点LIST found = init_LIST(P->NEXT->high, P->NEXT->low, P->NEXT->weight + 1);//删除LIST del = P->NEXT;P->NEXT = P->NEXT->NEXT;del->NEXT = NULL;free(del);return found;}}P = P->NEXT;}return tmp;
}
void insert(LIST head, LIST tmp)
{LIST P = head;while (P->NEXT){if (tmp->weight < P->NEXT->weight)break;P = P->NEXT;}//找不到位置if (!P->NEXT){P->NEXT = tmp;return;}//inserttmp->NEXT = P->NEXT;P->NEXT = tmp;
}
void output(LIST head)
{LIST P = head->NEXT;while (P){if ((char)P->high == '\n'){printf("字符 \\n 个数是%d\t占用字节为%d\t总字节为%d\n", P->weight, P->low == -1 ? 1 : 2, P->weight * (P->low == -1 ? 1 : 2));P = P->NEXT;continue;}printf("字符 ");putchar(P->high);if (P->high > 128)putchar(P->low);printf(" 个数是%d\t占用字节为%d\t总字节为%d\n", P->weight, P->low == -1 ? 1 : 2, P->weight * (P->low == -1 ? 1 : 2));P = P->NEXT;}printf("总字节数为%d\n", head->high + head->low * 2);
}
LIST init_LIST(int high, int low, int weight)
{LIST tmp = (LIST)malloc(sizeof(struct node));tmp->high = high;tmp->low = low;tmp->weight = weight;tmp->NEXT = NULL;return tmp;
}

数据结构第二次实验-赫夫曼编码及其应用相关推荐

  1. 舞伴配对问题c语言实训报告,C语言数据结构队列实现舞伴匹配(数据结构第二次实验)...

    C语言实验作业 题目如下: 课程名称:数据结构 实验目的: 1.掌握队列的定义及实现: 2.掌握利用队列的基本操作. 实验要求: 1.    使用链式结构完成队列的各种基本操作: 2.    补充完善 ...

  2. 西南交通大学数据结构第二次实验报告

    写在前面: 不想学了 事先说明:本次实验有进阶要求,即写出正常的正则表达式,但是本代码并没有完成这个进阶要求,只是做了一个基础版. 附加一句:其实CMakeLists是可有可无的,一般的IDE都是自动 ...

  3. 数据结构与算法 第二次实验报告堆栈队列

          数据结构与算法 第二次实验报告 姓名:许恺 学号:2014011329 班级:计算机14-1 中国石油大学(北京)计算机科学与技术系 前     言 <数据结构>是计算机及相关 ...

  4. R语言入门第二集 实验一:R 语言数据结构、数据导入与数据处理

    R语言入门第二集 实验一:R 语言数据结构.数据导入与数据处理 一.资源 R语言基本数据结构练习和数据的导入和处理对象常用函数练习--东北大学大数据班R语言实训第一次作业" R(4)求解数据 ...

  5. 数据结构java 图实验代码_20172329 2018-2019 《Java软件结构与数据结构》实验三报告...

    20172329 2018-2019-2 <Java软件结构与数据结构>实验三报告 课程:<Java软件结构与数据结构>html 班级: 1723java 姓名: 王文彬lin ...

  6. 数据结构与算法实验题 4.2 Who is the strongest

    数据结构与算法实验题 4.2 Who is the strongest ★实验任务 在神奇的魔法世界,召唤师召唤了一群的魁偶.这些魁偶排成一排,每个魁偶都有一个 战斗值.现在该召唤师有一个技能,该技能 ...

  7. 数据结构与算法实验题 6.1 鼹鼠掘土挑战赛

    数据结构与算法实验题 6..1 鼹鼠掘土挑战赛 ★实验任务 在潘多拉星的哈利路亚山上,生活着许多鼹鼠矿工,它们同属于潘多拉矿业公司哈利路 亚分公司.尽管在潘多拉星最美丽壮阔的地方工作,是一件十分让普通 ...

  8. 算法分析与设计 西工大 noj 第二次实验

    算法分析与设计 西工大 noj 第二次实验 Problem A 0-1背包问题 时限:1000ms 内存限制:10000K 总时限:3000ms 描述: 需对容量为c 的背包进行装载.从n 个物品中选 ...

  9. 数据结构第二章学习总结

    数据结构第二章学习总结 在数据结构第二章,我们学习了线性表的两种结构顺序和链式结构,学习了他们的定义,不同,存储结构和逻辑结构,还有一些基本的操作. 在学习过程中,我发现了许多问题:①书上的大多数代码 ...

最新文章

  1. Docker镜像优化
  2. 【把握未来】一文总结5G边缘计算的价值机遇
  3. 对ZNNT-5NM力矩传感器进行标定
  4. 日常生活 -- 面试思考
  5. Jerry的SAP One Order框架重构的几版代码
  6. c语言readline库6,readline-6.3 之arm平台交叉编译
  7. DateTimeFormatInfo 类
  8. LeetCode 66. 加一
  9. 35岁学嵌入式合适吗_什么是嵌入式技术?
  10. shell的if和else
  11. 人生也要一个中心两个基本点(转载)
  12. Linux之hugepage大页内存理论
  13. 面试题 计算机安全,XX计算机信息安全工程师面试题路由.doc
  14. centos 安装mysql5.6.22_CentOS 6.4 安装MySQL 5.6.22
  15. 火力发电计算机控制参数详细过程,火力发电过程
  16. c语言 常微分方程 库,使用C语言解常微分方程CODE.docx
  17. 用PS做一个简单的相机图标
  18. android隐藏其他应用图标,【Android】隐藏app图标以及隐式启动其他APP
  19. 计算机cpu intel,intel CPU后面带F是什么意思?Intel处理器后面带“F”含义详解
  20. linux查看wifi信号命令_详细解析:Linux系统的无线网络命令

热门文章

  1. 带你了解EMC——什么是EMC?
  2. 如何在邮件中嵌入 加入QQ群的按钮
  3. 软件测试管理工具alm,以知识为核心的ALM之测试管理篇
  4. 计算机屏幕的作用是什么情况,电脑屏幕分辨率的作用是什么?
  5. c语言作用域详解,C语言之作用域
  6. 【vue】纯前端登录验证码实现记录
  7. linux服务器删除cdn,有惊无险的一次后台删除操作!警告不要乱删除东西
  8. [bzoj4833][数论][min-max容斥]最小公倍佩尔数
  9. 关于友善之臂出的Nanopi R2S盒子的TTL乱码解决方法
  10. web开发从入门到精通一(详细)