实验目的:
1、 了解该树的应用实例,熟悉掌握Huffman树的构造方法及Huffman编码的应用,
2、 了解Huffman树在通信、编码领域的应用过程。
实验要求:
1、输入一段100—200字的英文短文,存入一文件a中。
2、写函数统计短文出现的字母个数n及每个字母的出现次数
3、写函数以字母出现次数作权值,建Haffman树(n个叶子),给出每个字母的Haffman编码。
4、用每个字母编码对原短文进行编码,码文存入文件b中。
5、用Haffman树对b中码文进行译码,结果存入文件c中,比较a,c是否一致,以检验编码、译码的正确性。
实验内容和实验步骤:
1.先编辑基本的存储单元struct twshu 用来存储节点;
2,在主函数中完成文件读写到数组中并且编码;编码中调用了quanzhi();来辅助;
3.在编写用来创建单链的twshu* creatnode(int q, char a, int type) //创建节点
与twshu* creatlink(int num, char word) { //创建第一条链表,这个链表是后面创建二叉树的基础,并且赋予权值和对应的字母,按权值大小排序。
4.编写创建二叉树的编码 twshu* creatwshu(twshu* link, int quan[52], char A[52]) { //创建二叉树,创建的思想为根据之前创建的有序link,最开始的两个最小,最小的两个形成新的节点并且接入到链表中
5.编写为每个字母编码的函数int bianma(twshu* head, char hfm[52][52], char A[52], char b[1], int i, int j) {//对每一个叶子对应的字母进行编码,编码方式为进行一遍先根遍历,每遍历一次左子树加是对应的哈夫曼编码加0,每遍历一遍右子树加一,如果遍历的节点是叶子的话使标记hfm编码数组的值加一并返回,
6.以哈夫曼码为基础,编写char *bianmafile(char hfm[52][52], char A[52], twshu* head, char str[N + 1]) {//根据哈夫曼码及其对应的字母对整个文件进行编码
7. 编写译码函数译码char* yima(char bstr[N * 52], twshu* head) {//根据编码文件,遍历译码树,当访问到叶子的时候返回相应的数值并且从头开始继续遍历
8.在主函数中依次进行权值计算,单链的创建,哈夫曼树的建立,为哈夫曼树编码,为文件编码,为文件译码这些过程,并且将文件存储到根目录下。
代码如下:
#include<stdio.h>
#include <string>
#include <math.h>
#define N 1000
struct twshu {
int number;
int type;//0是根,1是节点,2是叶子
char elem;
twshu* lchild, * rchild, * next = NULL;
};
void compare(twshu* link, int quan);
twshu* creatlink(int num, char word);
twshu* creatwshu(twshu* link, int quan[52], char A[52]);
int bianma(twshu* head, char hfm[52][52], char A[52], char b[1], int i, int j);
char* bianmafile(char hfm[52][52], char A[52], twshu* head, char str[N + 1]);
char yimashu(twshu* head, char bstr[N * 52], int& i);
char* yima(char bstr[N * 52], twshu* head);
int quanzhi(char str[N + 1], char elem) {//为每一个字母返回权值
int quan = 0;
int i = 0;
while (str[i] != NULL)
{
if (elem == str[i]) quan++;
i++;
}
return quan;
}
twshu* creatnode(int q, char a, int type) { //创建节点
twshu* node = ((twshu*)malloc(sizeof(twshu)));
node->type = type;
node->number = q;
node->elem = a;
node->rchild = NULL;
node->lchild = NULL;
node->next = NULL;
return node;
}
void compare(twshu* link, int quan) { //为运算一次之后的权值排序,截止条件是数值大于运算后的权值
twshu* p = link;
while (p != NULL && p->number <= quan) {
twshu* q = p;
while (q != NULL && q->number <= quan) {
if (p->number > q->number) {
twshu* lchild = p->lchild;
int number = p->number;
twshu* rchild = p->rchild;
char elem = p->elem;
int type = p->type;
p->lchild = q->lchild;
p->number = q->number;
p->rchild = q->rchild;
p->elem = q->elem;
p->type = q->type;
q->lchild = lchild;
q->number = number;
q->rchild = rchild;
q->elem = elem;
q->type = type;
}
q = q->next;
}
p = p->next;
}
}
twshu* creatlink(int num, char word) { //创建第一条链表,这个链表是后面创建二叉树的基础
twshu* node = (twshu*)malloc(sizeof(twshu));
node->lchild = NULL;
node->number = num;
node->rchild = NULL;
node->type = 2;
node->elem = word;
node->next = NULL;
return node;
}
twshu* creatwshu(twshu* link, int quan[52], char A[52]) { //创建二叉树,创建的思想为根据之前创建的有序link,最开始的两个最小,最小的两个形成新的节点并且接入到链表中
if (link->next != NULL)
{
twshu* temp = NULL;
twshu* q = link->next;
while (q->next != NULL) {
temp = (twshu*)malloc(sizeof(twshu));
temp->next = q->next->next;
temp->lchild = q;
temp->rchild = q->next;
q = temp;
q->number = quan[0] + quan[1];
q->type = 1;
q->elem = 0;
compare(q, q->number);
}
return q;
}
else {
return NULL;
}
}
int bianma(twshu* head, char hfm[52][52], char A[52], char b[1], int i, int j) {//对每一个叶子对应的字母进行编码,编码方式为进行一遍先根遍历,每遍历一次左子树加是对应的哈夫曼编码加0,每遍历一遍右子树加一,如果遍历的节点是叶子的话使标记hfm编码数组的值加一并返回,
twshu* p = head;
j++;
if (b [0] != '2') {
hfm[i][j] = b[0];
}
if (head->lchild == NULL && head->rchild == NULL)
{
A[i] = head->elem;
int t = 0;
for (int t = 0; t < j; t++)
{
if (i != 0 && hfm[i][t] == NULL)
{
hfm[i][t] = hfm[i - 1][t];
}
}
i++;
return i;
}
int t = j;
char b0[1] = { '0' };
char b1[1] = { '1' };
i = bianma(head->lchild, hfm, A, b0, i, j);
j = t;
i = bianma(head->rchild, hfm, A, b1, i, j);
return i;
}
char *bianmafile(char hfm[52][52], char A[52], twshu* head, char str[N + 1]) {//根据哈夫曼码及其对应的字母对整个文件进行编码
char bstr[52 * N]={};
int i = 0, j = 0, t = 0, n = 0;
while (str[i] != NULL) {
t = 0;
while (A[t] != str[i] && t < 52) { t++; }
if (t < 52) {
while (hfm[t][n] != NULL) {
bstr[j] = hfm[t][n];
n++;
j++;
}
}
n = 0;
i++;
}
return bstr;
}
char yimashu(twshu* head, char bstr[N * 52], int& i) {
if (head->lchild == NULL && head->rchild == NULL) return head->elem;
if (bstr[i] == '0') {
i++;
return yimashu(head->lchild, bstr, i);
}
else {
i++;
return yimashu(head->rchild, bstr, i);
}
}
char* yima(char bstr[N * 52], twshu* head) {//根据编码文件,遍历译码树,当访问到叶子的时候返回相应的数值并且从头开始继续遍历
char xstr[N+1]={};
int i = 0, j = 0;
while (bstr[i] != NULL) {
xstr[j] = yimashu(head, bstr, i);
j++;
}
return xstr;
}
int main()
{
FILE* f;
char str[N + 1] = {};
char astr[N + 1] = {};
char fstr[N + 1] = {};
int quan[52], i = 0, sum = 0, j = 0;
char hfm[52][52] = {};
char A[52]={};
int one = 1;
char* bstr;
char cstr[52 * N]={};
char* xstr;
errno_t err = fopen_s(&f, "data.txt", "r");
for (i = 0; i < 52; i++)
{
quan[i] = 0;
}
i = 0;
if (f != NULL)
{
printf("fopen ok\n");
}
char c;
while ((c = fgetc(f)) != EOF && i < 999) { // 逐个读取字符并存储到数组中
fstr[i] = c;
i++;
}
printf("文件中的原内容为\n");
printf("%s\n", fstr);
i = 0;
while (fstr[i] != '/0'&&i<N) {//计算每个字母的权值
one = 1;
int j = 0;
while (A[j] != NULL) {
if (fstr[i] == A[j]) one = 0;
j++;
}
if (one) {
if ((fstr[i] >= 'a' && fstr[i] <= 'z') || (fstr[i] >= 'A' && fstr[i] < ='Z'))
{
A[sum] = fstr[i];
quan[sum] = quanzhi(fstr, fstr[i]);
sum++;
}
}
i++;
}
sum = 0;
for (i = 0; A[i] != NULL && quan[i] != NULL; i++) {//按权值大小排序
for (int j = i + 1; A[j] != NULL && quan[j] != NULL; j++) {
if (quan[i] > quan[j]) {
int temp = quan[i];
char TEMP = A[i];
A[i] = A[j];
quan[i] = quan[j];
A[j] = TEMP;
quan[j] = temp;
}
}
}
twshu* link = creatlink(0, 0);
twshu* p = link;
i = 0;
while (A[i] != NULL) { //将权值连成链表
p->next = creatlink(quan[i], A[i]);
p = p->next;
i++;
}
twshu* head = creatwshu(link, quan, A);//返回哈夫曼树
if (head != NULL) {
head->elem = 0;
head->type = 0;
}
else {
printf("创建树失败");
}
char b[1] = { '2' };
bianma(head, hfm, A, b, 0, -2);
printf("bianma操作:输出每个字母对应的编码\n");//输出每个字母对应的编码
i = 0;
while (A[i] != NULL) {
printf("%c=%s\n", A[i], hfm[i]);
i++;
}
i = 0; j = 0;
if (f != 0)
fclose(f);
bstr = bianmafile(hfm, A, head, fstr);
err=fopen_s(&f,"databianma.txt", "w");
if (f != NULL) {
printf("\n写入bianma文件打开成功\n");
fwrite(bstr, sizeof(char), sizeof(cstr), f);
fclose(f);
}
err = fopen_s(&f,"databianma.txt", "r");
i = 0;
while ((c = fgetc(f)) != EOF && i < 5000) { // 逐个读取字符并存储到数组中
cstr[i] = c;
i++;
}
bstr = cstr;
fclose(f);
printf("\n\n编码结果:\n%s", bstr);
i = 0;
xstr = yima(bstr, head);
int size = 0;
while (xstr[size] != NULL)
{
astr[size] = xstr[size];
size++;
}
err = fopen_s(&f,"datayima.txt", "w");
printf("\n译码的结果:\n%s", astr);
if (f != NULL) {
printf("\n写入文件已经打开\n");
fprintf(f, " % s", astr);
fclose(f);
}
printf("\nendend\n");
return 0;
}