Huffuman Coding(哈夫曼编码)

来源:互联网 发布:淘宝嘉年华时间买家 编辑:程序博客网 时间:2024/05/16 05:15

哈夫曼编码:

假设要给一偏文章进行编码,文章由英文组成,对于全文来说,我们可以统计得到这篇文章各个字母出现的个数,个数大的字母意味着它在文章中出现的频率更高,对所有的字母都用相同的空间大小编码的话会产生一定的空间浪费。哈夫曼编码使得高频率出现的字母用更短码值表示,低频率出现的字母用更长的码值表示,可以缩小整篇文章的空间占用,达到压缩的目的。把字母看做编码用的各个编码数值同理。

哈夫曼树定义:

给定n个自带权值的节点构造一个二叉树,若带权路径长度最小,称这样的树为哈夫曼树或者最优二叉树。哈弗曼树是权值最小的二叉树,权值越大的节点离根节点越近。

基本概念:

  1. 路径和路径长度:在一棵树中,从一个节点往下可以到达的孩子或者孙子节点之间的通路,成为路径。通路中分支路径的个数成为路径长度。
  2. 节点的权与带权路径长度:若将树中节点赋予一个有意义的数值,则这个数值成为这个节点的权。节点的带权路径长度为节点当前权值乘上路径长度。
  3. 树的带权路径长度: 树的带权路径长度为所有叶节点的带权路径长度之和,几位WPL。

哈夫曼树的构造:

  1. 把n个节点看做n棵二叉树,此时每棵树只有一个节点
  2. 在森林中选择两个权值最小的树进行合并,形成新的二叉树,该树的根节点的权值为合并的左右树的权值之和
  3. 从森林中删去合并的两棵树,并加入合并后的那棵新树
  4. 重复2、3步直到森林中只剩一棵树结束,哈夫曼树构造完成。

哈弗曼编码:

先统计带压缩文件的各个节点的权值,生成一个哈夫曼树;根据单词的各个编码字符,从节点开始,找到这个节点的parent节点在比较原节点是父节点的左孩子还是右孩子,一般规定是左孩子为0,右孩子为1,比较到根节点为止,得到的编码是最终编码的倒序,进而得到每个节点的哈夫曼编码。之后结点的个数,符号,以及编码写在哈夫曼压缩文件头部,供文件解压的时候使用;之后开始读入压缩文件,将已有的字符对应的哈夫曼编码写入待输出二进制文件完成压缩。哈夫曼解码的时候根据文件头的信息生成哈夫曼树,因为哈夫曼编码为前缀编码,之后从根节点开始将编码转化成一个个字符(哈夫曼树的叶子节点),以此类推,解压完成。

源程序代码:

#include<stdio.h>#include<string.h>#include<stdlib.h>#define maxsize 20001typedef struct BTnode{    int weight;//权重     int parent;//父节点     int lchild;//子节点     int rchild;    unsigned char a;}BTnode;typedef struct CharNode{    char bit[maxsize];    unsigned char a;    int weight; }CharNode;//字符节点(存储权重与编码) BTnode BT[maxsize];CharNode Dictionary[maxsize];int root;int node_num;long File_Point;void get_weight(char* input)//读取未压缩文件进行的权重生成 {    printf("字符权重计算中...\n") ;    unsigned char temp, pre;    FILE *fp;    fp = fopen(input, "rb");    if(fp == NULL)    {        printf("Input Error\n");        exit(0);    }    while(!feof(fp))    {        fread(&temp, sizeof(unsigned char), 1, fp);        if(temp == '\0')            continue;        BT[temp].weight++;        BT[temp].a = temp;        temp = '\0';        }    fclose(fp);    printf("计算权重已完成\n");}void HuffmanTree()//压缩文件过程中源文件生成的霍夫曼树 {    printf("正在构建哈夫曼树...\n");    BTnode t;    int minweight;    int lchild, rchild;    int k=0;    for(int i = 0; i<maxsize ;i++)//冒泡排序将叶子节点按照从大到小顺序排列     {        for(int j = 0; j<maxsize; j++)        {            if(BT[j].weight<BT[j+1].weight)            {                t = BT[j+1];                BT[j+1] = BT[j];                BT[j] = t;              }           }    }    while(BT[k].weight!=0)        k++;    node_num=k;//统计字符个数     root = 2*k-2;//k个叶子节点k-1个内部节点     for(int i=k; i<2*k-1; i++)//接下去两个for循环取到两个权值最小节点     {         minweight = maxsize;         lchild = -1;         rchild = -1;         for(int j=0; j<i; j++)         {            if(BT[j].weight<minweight && BT[j].parent==-1)            {                minweight = BT[j].weight;                lchild = j;            }         }         minweight = maxsize;//将该变量重置为max值以便于后面比较取值          BT[lchild].parent = i;         for(int h=0; h<i; h++)         {            if(BT[h].weight<minweight &&BT[h].parent==-1)            {                minweight = BT[h].weight;                rchild = h;             }         }         BT[rchild].parent = i;         BT[i].weight = BT[lchild].weight + BT[rchild].weight;         BT[i].lchild = lchild;         BT[i].rchild = rchild;    }    BT[root].parent = maxsize;    printf("哈夫曼树完成构建\n");}void coding(){//哈弗曼编码     printf("正在进行哈弗曼编码...\n");    int ndoe = 0;    int findp, itself, point;    int end;    char temp;    for(int node = 0; node<=root; node++)    {        if(BT[node].a != NULL)        {            Dictionary[BT[node].a].a = BT[node].a;            Dictionary[BT[node].a].weight = BT[node].weight;            point=0;            findp = BT[node].parent;            itself = node;            while(findp <= root)            {                if(BT[findp].lchild == itself)//如果该节点是叶子节点                 {                    Dictionary[BT[node].a].bit[point] = '0';                    point ++;                }                else if(BT[findp].rchild == itself)                {                    Dictionary[BT[node].a].bit[point] = '1';                    point ++;                }                findp = BT[findp].parent;                itself = BT[itself].parent;            }        }    }    for(int i=0; i<maxsize; i++)    {        if(strlen(Dictionary[i].bit)!=0)        {            for(int j=0; j<strlen(Dictionary[i].bit); j++)            {                for(int k=0; k<strlen(Dictionary[i].bit)-j-1; k++)                {                    temp = Dictionary[i].bit[k];                    Dictionary[i].bit[k] = Dictionary[i].bit[k+1];                    Dictionary[i].bit[k+1] = temp;                }            }        }    }    printf("哈弗曼编码已完成\n");    for(int i=0; i<maxsize; i++)    {        if(Dictionary[i].weight!=0)        {            printf("符号:%c\t出现频率:%d\t哈夫曼编码%s\n", Dictionary[i].a, Dictionary[i].weight, Dictionary[i].bit);        }     } }void Output(char* input)//压缩后的二进制编码生成二进制文件 {    unsigned char temp;    unsigned char output;    char out_dat[maxsize];    int num = 0;    int all = 0;    FILE *fp, *out;    printf("生成文件名:");    scanf("%s", out_dat);    strcat(out_dat, ".dat");    printf("生成压缩文件%s\n", out_dat);    temp = '\0';    fp = fopen(input, "rb");    out = fopen(out_dat, "wb");     if(fp == NULL || out == NULL)    {        printf("输入文件名错误或建立新文件失败\n");        exit(0);    }    fwrite(&node_num, sizeof(int), 1, out);//将节点个数写入哈弗曼压缩文件     for(int i=0; i<node_num; i++)//压缩文件中写入各节点的字符和权重     {        fwrite(&BT[i].a, sizeof(unsigned char), 1, out);        fwrite(&BT[i].weight, sizeof(int), 1, out);    }    while(!feof(fp))    {        fread(&temp, sizeof(unsigned char), 1, fp);//      printf("%d:", temp);//555555555555555555555555555555555555555555555555 //      printf("%s\n", Dictionary[temp].bit);        if(temp == '\0')            continue;        all++;        for(int i=0; i<strlen(Dictionary[temp].bit); i++)        {            output <<= 1 ;            if(Dictionary[temp].bit[i] == '1')            {                output +=1;//              printf("1");////////////////////////////////            }//          else printf("0");/////////////////////////////////            num++;            if(num==8)            {                 fwrite(&output, sizeof(unsigned char), 1, out);//              printf("\n");//////////////////////////////////                num = 0;            }        }        temp = '\0';    }        if(num!=0){             for(int i=8; i>num; i--)            {                output <<=1;//              printf("0");//////////////////////////////////             }             fwrite(&output, sizeof(unsigned char), 1, out);        }           fwrite(&all, sizeof(int), 1, out);        printf("\n压缩文件成功\n"); }void Form_Huffman(char *input){    printf("生成霍夫曼树...\n");    FILE *fp;    BTnode t;    int num, minweight, all;    int rchild, lchild;    unsigned char a;    int weight;    fp = fopen(input, "rb");    fseek(fp, -4, 2);    fread(&all, sizeof(int), 1, fp);//  printf("%d\n", all);    fseek(fp, 0, 0);    fread(&num, sizeof(int), 1, fp);    for(int i=0; i<num; i++)    {        fread(&a, sizeof(unsigned char), 1, fp);        fread(&weight, sizeof(int), 1, fp);        BT[i].a = a;        BT[i].weight = weight;    }    root = num;    for(int i = 0; i<num ;i++)    {        for(int j = 0; j<num-1; j++)        {            if(BT[j].weight<BT[j+1].weight)            {                t = BT[j+1];                BT[j+1] = BT[j];                BT[j] = t;              }           }    }       for(int i=num; i<2*num-1; i++)    {         minweight = maxsize;         lchild = -1;         rchild = -1;         for(int j=0; j<i; j++)         {            if(BT[j].weight<minweight && BT[j].parent==-1)            {                minweight = BT[j].weight;                lchild = j;            }         }         minweight = maxsize;         BT[lchild].parent = i;         for(int h=0; h<i; h++)         {            if(BT[h].weight<minweight &&BT[h].parent==-1)            {                minweight = BT[h].weight;                rchild = h;             }         }         BT[rchild].parent = i;         BT[i].weight = BT[lchild].weight + BT[rchild].weight;         BT[i].lchild = lchild;         BT[i].rchild = rchild;         root = i;    }    printf("成功重构霍夫曼树\n"); //  for(int i=0; i<num; i++)//  {//      printf("%d\n", BT[i].a);//  }    printf("还原源文件中...\n");    int root_head;    char Origin_Code[maxsize];    unsigned char copy, judge;    FILE *out;    printf("输入您想输出的文件名称:");    scanf("%s", Origin_Code);    out = fopen(Origin_Code, "w");    if(out == NULL)    {        printf("Input Error\n");        exit(0);    }    root_head = root;    while(!feof(fp))    {        fread(&copy, sizeof(unsigned char), 1, fp);        for(int i=1; i<=8; i++)        {            judge = copy&10000000;            copy<<=1;            if(judge>0)            {                root_head = BT[root_head].rchild;//              printf("1");///////////////            }            else if(judge==0)            {                root_head = BT[root_head].lchild;//              printf("0");/////////////////            }            if(BT[root_head].a !=NULL)            {                fwrite(&BT[root_head].a, sizeof(unsigned char), 1, out);                all--;                root_head = root;                 if(all==0)                    break;            }//          if(i==8)//          {//              printf("\n");//          }               }        if(all==0)            break;      }    printf("还原源文件成功\n");}int main(){    int num=0;     char File_Name[maxsize];    int choose=0;    printf("=================================================================\n");    printf("  请输入您想要完成的功能:1、压缩文件;2、解压文件;3、退出程序\n");    printf("\n  注意:(1)压缩文件名带后缀;\n\t(2)压缩文件名默认.dat格式;\n\t(3)带压缩文件名带后缀;\n");    printf("=================================================================\n");    printf("输入:");    scanf("%d", &choose);    memset(File_Name, 0, sizeof(File_Name));    root = 0;    for(int i = 0; i<maxsize; i++)//初始化节点数组     {        BT[i].a = NULL;        BT[i].lchild=-1;        BT[i].parent=-1;        BT[i].rchild =-1;        BT[i].weight = 0;    }    for(int i=0; i<maxsize; i++)    {        Dictionary[i].a = NULL;        Dictionary[i].weight=0;        Dictionary[i].bit[0] = '\0';     }     switch(choose)    {        case 1://压缩文件             printf("请输入待压缩文件名:");            scanf("%s", File_Name);            get_weight(File_Name);//获取各个字符的权重             HuffmanTree();            coding();            Output(File_Name);//          for(int i=0; i<maxsize; i++)//          {//              if(Dictionary[i].a!=NULL)//              {//                  printf("%d:", Dictionary[i].a);//                  printf("%s", Dictionary[i].bit);//                  printf("\n");//              }//          }            break;        case 2://解压缩文件             printf("输出待解压文件名:");            scanf("%s", File_Name);            Form_Huffman(File_Name);            break;        case 3:            return 0;        default :            choose = 0;            printf("输入错误请重新输出数据\n");            break;                          }    return 0; } 
原创粉丝点击