数据结构第五周笔记(2)——树(下)(慕课浙大版本--XiaoYu)

164 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

5.2 哈夫曼树与哈夫曼编码

5.2.1 什么是哈夫曼树(Huffman Tree)

[例]将百分制的考试成绩转换成五分制的成绩

if(score < 60 ) grade = 1;
else if(score < 70) grade = 2;
else if(score < 80) grade = 3;
else if(score < 90) grade = 4;
else grade = 5;

上述代码中,其实对应着就有一棵树,如下图:

image-20220707234237931

image-20220707234356578

优化后的效率:

image-20220707234452711

if(score < 80)
{
    if(score < 70 )
        if(score < 60) grade = 1;
    else grade = 2;
}else if(score < 90 )grade = 4;
else grade = 5;

如何根据结点不同的查找频率构造更有效的搜索树?

哈夫曼树的定义

image-20220707234921567

最优二叉树或哈夫曼树就是WPL最小的二叉树

一棵二叉树每一个叶结点的频率或者权重乘以这个叶结点到根结点的这个路径的长度就是带权路径的长度

权值 = 频率

[例]有五个叶子结点,它们的权值为{1,2,3,4,5},用此权值序列可以构造出形状不同的多个二叉树

image-20220708001419786

image-20220708001539908是50

image-20220708001741632

5.2.2 哈夫曼树的构造

  1. 每次把权值最小的两颗二叉树合并

  2. image-20220708002041082

  3. 代码实现(如何选取两个最小的?)

    1. typedef struct TreeNode *HuffmanTree;
      struct TreeNode{
          int Weight;
          HuffmanTree Left,Right;
      }
      HuffmanTree Huffman(MinHeap H)
      {
          //假设H->Size个权值已经存在H->Elements[]->Weight里
          int i; HuffmanTree T;
          BuildMinHeap(H);//将H->Elements[]按权值调整为最小堆
          for(i = 1;i < H->Size; i++){//做H->Size-1次合并
              T = malloc(sizeof(struct TreeNode));//建立新结点
              T->Left = DeleteMin(H);//从最小堆中删除一个结点,作为新T的左子结点
              T->Right = DeleteMin(H);//从最小堆中删除一个结点,作为新T的右子结点
              T->Weight = T->Left->Weight+T->Right->Weight;//计算新权值
              Insert(H,T);//将新T插入最小堆
          }
          T = DeleteMin(H);
          return T;
      }
      整体复杂度为O(NlogN)
      

      哈夫曼树的特点:

      1. 没有度为1的结点;

      2. n个叶子结点的哈夫曼树共有2n-1个结点

        1. n0:叶结点总数
        2. n1:只有一个儿子的结点总数
        3. n2:有2个儿子的结点总数
        4. n2 = n0 - 1
      3. 哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树;

      4. image-20220708003623357

5.2.3 哈夫曼编码

给定一段字符串,如何对字符进行编码,可以使得该字符串的编码存储空间最少?

image-20220708003841443

分析:

  1. 用等长ASCII编码:58×8 = 464位
  2. 用等长3位编码:58×3 = 174位;
  3. 不等长编码:出现频率高的字符用的编码短些,出现频率低的字符则可以编码长些?

怎么进行不等长编码?

如何避免二义性(就是你这个编码不止一个意思)

  1. 前缀码prefix code:任何字符的编码都不是另一字符编码的前缀

    1. 可以无二义地解码(你的这个编码不能是其他编码的前缀)
  2. 二叉树用于编码:

    1. 左右分支:0、1

    2. 字符只在叶结点上

    3. image-20220708004934296

    4. image-20220708005100967

    5. image-20220708005137719

    6. 怎么构造一颗编码代价最小的二叉树?

      1. image-20220708005631723

小测验:哈夫曼树

image-20220708010907498