• 哈夫曼树编码-C语言


    哈夫曼树编码

    1.实验目的

    了解二叉树的定义,理解二叉树的基本性质和存储结构,掌握哈夫曼树的构造,实现哈夫曼编码与译码算法。

    2.实验内容

    从键盘输入一串电文字符与权值,输出对应的哈夫曼编码;从键盘输入一串二进制代码,输出对应的电文字符串。具体步骤如下:

    1. 构造一棵哈夫曼树;
    2. 实现哈夫曼编码;
    3. 对哈夫曼编码生成的二进制串进行译码;
    4. 要求程序中字符和权值是可变的,实现程序的灵活性。

    3.实验工具

    Dev-C++

    4.实验代码

    //Authors:xioabei
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    typedef char **HuffmanCode;   //动态分配数组存储哈夫曼编码表 
    typedef struct{
     int weight;
     int parent,lchild,rchild;
    }HTNode,*HuffmanTree;
    typedef struct Code{
     char ch;
     int weight;
     Code *next;
    }*CodeLink;
    //创建字符与权值 
    void CreateCouple(CodeLink &Couple,int n){
     int i;
     CodeLink r,p;
     Couple = (CodeLink)malloc(sizeof(Code));
     Couple->next = NULL;
     r = Couple;
     for(i = 1;i<=n;i++){
      p = (CodeLink)malloc(sizeof(Code));
      printf("[请输入第%d个字符与权值:]
    >>>",i);
      getchar();
      scanf("%c %d",&Couple[i].ch,&Couple[i].weight);
      p->next = NULL;
      r->next = p;
      r = p;
     }
     printf("
    [成功创建字符与权值]
    ");
    }
    //选择最小的两个 
    void Select(HuffmanTree HT,int n,int *s1, int *s2){
     int i,min,max;
     for(i=1;i<=n;i++){
      if(HT[i].parent == 0){
       *s2 = i;
       max = i;
       *s1 = i;
      }
     }
     for(i=1;i<=n;i++){
      if(HT[*s1].weight>HT[i].weight && HT[i].parent == 0)
       *s1 = i;
      if(HT[max].weight<HT[i].weight && HT[i].parent == 0)
       max = i;
     }
     min = HT[*s1].weight;
     HT[*s1].weight = HT[max].weight;
     for(i=1;i<=n;i++)
      if(HT[*s2].weight>HT[i].weight && HT[i].parent == 0)
       *s2 = i;
     HT[*s1].weight = min;
     printf("%d--%d
    ",HT[*s1].weight,HT[*s2].weight);
    }
    //创建哈夫曼树 
    void CreateHuffmanTree(HuffmanTree &HT,CodeLink Couple,int n){
     printf("
    ---------开始创建哈夫曼树---------
    ");
     int m,i,s1=1,s2=1,*p = &s1,*q = &s2;
     //构造哈夫曼树HT
     if(n<=1)
      return;
     m=2*n-1;
     HT =  (HTNode*)malloc(sizeof(HTNode)*(m+1)); //由于0号单元未用,所以需要动态分配m+1个单元,HT[m]表示根结点 
     for(i=1;i<=m;i++){       //将1~m号单元初始化为0 
      HT[i].parent = 0;
      HT[i].lchild = 0;
      HT[i].rchild = 0;
     }
     printf("
    初始化成功……
    "); 
     for(i=1;i<=n;i++)       //输入前n个单元叶子结点权值
      HT[i].weight = Couple[i].weight;
    //---------- 初始化工作结束,开始创建哈夫曼树 -----------// 
     for(i=n+1;i<=m;i++){
    //  通过n-1次选择、删除、合并来创建哈夫曼树
      Select(HT,i-1,p,q);
    //  在HT[k](1<=k<=i-1)中选择双亲域为0且权值最小的结点,并且返回它们在HT中序号s1,s2
      HT[s1].parent = i;
      HT[s2].parent = i;
    //  得到新结点i,从森林中删除s1,s2,将s1,s2双亲域由0改为i
      HT[i].lchild = s1;      //s1,s2分别作为i的左右孩子 
      HT[i].rchild = s2;
      HT[i].weight = HT[s1].weight + HT[s2].weight; //i的权值为左右孩子权值之和 
     }
     printf("
    ---------结束创建哈夫曼树---------
    ");
    }
    //创建哈夫曼编码 
    void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
     int c,f,start,i;
     char *cd; 
     //从叶子结点逆向求每个字符的哈夫曼编码,存储在编码表HC中 
     HC = (HuffmanCode)malloc(sizeof(char*)*(n+1));  //分配n个字符编码表空间 
     cd = (char*)malloc(sizeof(char)*n);    //分配临时存放每个字符编码动态数组空间 
     cd[n-1] = '';         //编码结束符 
     for(i=1;i<=n;i++){        //逐个字符求哈夫曼编码 
      start = n-1;   //start开始指向最后,即编码结束位置 
      c = i;
      f = HT[i].parent;  //f指向c的双亲结点
      while(f!=0){   //从叶子结点开始向上回溯,直到根结点 
       --start;   //回溯一次start向前指一个位置 
       if(HT[f].lchild==c)
        cd[start] = '0';//结点c是f的左孩子,则生成代码"0" 
       else
        cd[start] = '1';//结点c是f的右孩子,则生成代码"1" 
       c = f;
       f = HT[f].parent; //继续向上回溯 
      }      //求出第i个字符编码 
      HC[i] = (char*)malloc(sizeof(char)*(n-start)); //为第i个字符编码分配空间 
      strcpy(HC[i],&cd[start]);//将求得的编码从临时空间cd中复制到HC当前行中
      puts(HC[i]);
     }
     free(cd);     //释放临时空间 
     printf("
    ---------哈夫曼树编码成功---------
    ");
    }
    //编码 
    void Encode(HuffmanCode HC,CodeLink Couple,char T[],int n){
     int i,j,k,t;
     printf("[编码如下:]
    ");
     for(i=0;T[i]!='';i++)
      for(j=1;j<=n;j++){
       if(Couple[j].ch==T[i]){
        for(k=1;k<=n;k++){
         if(Couple[j].weight==Couple[k].weight)
          puts(HC[k]);
        }
       } 
     }
    }
    //译码 
    void Decode(HuffmanCode HC,CodeLink Couple,char T[],int n){
     int i,j,k,location = 0,len,tag;
     printf("[译码如下:]
    ");
     for(i=0;T[location+1]!='';i++){
      for(j=1;j<=n;j++){
       len = strlen(HC[j]);
       tag = 1;
       for(k=0;k<len;k++){
        if(HC[j][k]!=T[location+k]){
         tag = 0;
         break;
        }
       }
       if(tag==1){
        printf("%c",Couple[j].ch);
        break;
       }
      }
      len = strlen(HC[j]);
      location += len;
     }
    }
    // 打印菜单 
    void PrintMenu(){
     printf("
    **********菜单**********
    ");
     printf("
    1.创建字符与权值;
    ");
     printf("2.创建哈夫曼树;
    ");
     printf("3.生成哈夫曼编码;
    ");
     printf("4.编码;
    ");
     printf("5.译码;
    ");
     printf("0.退出;
    ");
     printf("
    ************************
    ");
     printf("[请输入你的选择:]
    >>>");
    }
    //主函数 
    int main(){
     HuffmanTree HT;
     HuffmanCode HC;
     CodeLink Couple; 
     int i,n,user;
     char T[100];
     while(1){
      PrintMenu();
      scanf("%d",&user);
      switch(user){
       case 1:{
        printf("[请输入叶子结点数:]
    >>>");
        scanf("%d",&n);
        CreateCouple(Couple,n);
        break;
       }
       case 2:CreateHuffmanTree(HT,Couple,n);break;
       case 3:CreateHuffmanCode(HT,HC,n);break;
       case 4:{
        printf("[请输入要编码的字符:]
    >>>");
        getchar();
        gets(T);
        Encode(HC,Couple,T,n);
        break;
       }
       case 5:{
        printf("[请输入要编码的字符:]
    >>>");
        getchar();
        gets(T);
        Decode(HC,Couple,T,n);
        break; 
       } 
       case 0:exit(0);
      }
     }
     return 0;
    }

    5.实验结果

    Haffuman

    Haffuman

    6.实验分析

    1.HT初态

    HT初态
    2.HT终态
    HT终态
    3.示意图
    哈夫曼树

    7.资料

    1951年,哈夫曼在麻省理工学院(MIT)攻读博士学位,他和修读信息论课程的同学得选择是完成学期报告还是期末考试。导师罗伯特·法诺(Robert Fano)出的学期报告题目是:查找最有效的二进制编码。由于无法证明哪个已有编码是最有效的,哈夫曼放弃对已有编码的研究,转向新的探索,最终发现了基于有序频率二叉树编码的想法,并很快证明了这个方法是最有效的。哈夫曼使用自底向上的方法构建二叉树,避免了次优算法香农-范诺编码(Shannon–Fano coding)的最大弊端──自顶向下构建树。

    给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

    在计算机数据处理中,哈夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

  • 相关阅读:
    数据库数据实时采集Maxwell
    消息中间件之Kafka相关知识
    linux安装软件配置
    ETL工具Sqoop
    Hadoop 概述(三)
    全网最全的权限系统设计方案(图解)
    WebSocket 是什么原理?为什么可以实现持久连接
    封装 axios 拦截器实现用户无感刷新 access_token
    明明加了唯一索引,为什么还是产生了重复数据?
    动态组件和插槽
  • 原文地址:https://www.cnblogs.com/slz99/p/12527731.html
Copyright © 2020-2023  润新知