赫夫曼(Huffman)树/编码

目录点这里:【数据结构与算法】相关文章目录

一 . 基本概念:

    赫夫曼树:给定带权的N个叶子构成的所有二叉树中,树的带权路径长度最小的二叉树(最优二叉树)

    带权路径长度:所有树叶到树根之间的路径长度与该节点上权的乘积

    :赋予节点的有意义的参数

 

二 . 赫夫曼树的构造

    例:设权值集合{2,4,5,7}

    1.根据权值进行排序,取最小的两个叶子(2,4),较小的在左,较大的在右。添加一个新节点,节点的权值为它俩的权值和,构成一颗二叉树:

                                             赫夫曼(Huffman)树/编码_第1张图片

    2.用得到的节点作为新的叶子,重复第一步:

                                               赫夫曼(Huffman)树/编码_第2张图片

    3.一直重复构造,直到所有子叶都取完

                                            赫夫曼(Huffman)树/编码_第3张图片

    所以共需要进行n-1次构造,总共有 n+n-1 = 2n -1 个节点;

    可以证明,这样构造的二叉树是带权路径长度最小的二叉树

 

三 . 赫夫曼编码

    1.数据压缩(编码):把文件中的每个字符转换成一个唯一的二进制位串,且串里面不能包含其它字符的表示串。

    2.赫夫曼编码方法:如上例,设权为字符在文档中的重复次数,权值集合为{A:2,B:4,C:5,D:7},从根节点开始向左是0,向右是1

                                                       赫夫曼(Huffman)树/编码_第4张图片

                那么

                         A的编码为:110。

                         B的编码为:111。

                         C的编码为:10。

                         D的编码为:0。

    3.代码实现:(C语言)

        头文件等:

#include 
#include 
#define MAXINT 32767;

typedef struct
{
  char Character;
  unsigned int Weight;//权重
  unsigned int Parent, Lchild, Rchild;//双亲节点,左子节点,右子节点
}HTNode;

 

        a.生成霍夫曼树:

 

// n : 总叶子数  m: 用于构造Huffman树的数组大小(2n-1)
//k, j : 循环变量
//C, W : 接收字符和权时的中转变量
//Min_w1, Min_w2 : 用于存储两个最小权
//p1, p2 : 存储两个最小权数组的下标
void Great_Huffman(unsigned int n, HTNode HT[])
{
  int k , j;
  unsigned int W;
  char C;
  int m = 2*n - 1;
  //输入n个字符及其权
  for(k = 0; k < n; k++)
  {
    printf("input char and Weight : ");
    while((C = getchar()) == '\n');
    scanf("%d", &W);
    HT[k].Character = C;
    HT[k].Weight = W;
    HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0;//初始化
  }
  //初始化之后用于连接子叶的n-1个节点
  for(k = n; k < m; k++)
  {
    HT[k].Character = '\0';
    HT[k].Weight = 0;
    HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0;
  }
  //构造赫夫曼树
  int Min_w1, Min_w2;
  int p1, p2;
  //外层循环用于构造新的节点
  for(k = n; k < m; k++)
  {
    Min_w1 =  Min_w2 = MAXINT;//初始化最小值
    p1 = p2 = 0;//初始化下标
    //内层循环用于寻找构造新节点的两个最小权节点
    for(j = 0; j < k; j++)
    {
      if(HT[j].Parent == 0)//如果这个子叶尚未合并
      {
        if(HT[j].Weight < Min_w1)//更新最小权
        {
          Min_w2 = Min_w1;
          p2 = p1;
          Min_w1 = HT[j].Weight;
          p1 = j;
        }
        else if(HT[j].Weight < Min_w2)//更新次小权
        {
          Min_w2 = HT[j].Weight;
          p2 = j;
        }
      }
    }
    HT[p1].Parent = HT[p2].Parent = k;
    HT[k].Lchild = p1; HT[k].Rchild = p2;
    HT[k].Weight = HT[p1].Weight + HT[p2].Weight;
  }
}

    (注意输入格式,字符和权用空格隔开)

   赫夫曼(Huffman)树/编码_第5张图片

    b.生成霍夫曼编码

//k : 循环变量
//sp : 编号指针,指向目前编号位,从后往前
//fp :双亲的下标
//p : 当前叶子下标
//HF : 暂时存储当前子叶的赫夫曼编码
void Huffman_Coding(unsigned int n, HTNode HT[])
{
  int k;
  int sp, fp;
  char *HF;//当前编码
  HF = (char *)malloc(n*sizeof(char));
  int p;
  for(k = 0; k < n; k++)
  {
    sp = n-1; p = k; fp = HT[k].Parent;
    //从叶子搜索直到根节点
    while(fp != 0)
    {
      if(HT[fp].Lchild == p)//如果当前节点是左子树
        HF[sp] = '0';
      else
        HF[sp] = '1';
      sp--;
      fp = HT[fp].Parent;
      p = fp;
    }
    //显示编码
    printf("\n%c : ", HT[k].Character);
    sp++;
    while(sp < n)
    {
      printf("%c",HF[sp]);
      sp++;
    }
  }
}

 

你可能感兴趣的:(数据结构,算法设计)