基本介绍

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

赫夫曼树的几个重要概念

  1. 路径:在一棵树中,一个节点到另一个节点之间的通路,称为路径。下图中,从根节点到节点 a 之间的通路就是一条路径。
  2. 路径长度:在一条路径中,每经过一个节点,路径长度都要加 1 。例如在一棵树中,规定根节点所在层数为1层,那么从根节点到第 i 层节点的路径长度为 i - 1 。下图中从根节点到节点 c 的路径长度为 3。
  3. 节点的权:给每一个节点赋予一个新的数值,被称为这个节点的权。例如,下图中节点 a 的权为 7,结点 b 的权为 5。
  4. 节点的带权路径长度:指的是从根节点到该节点之间的路径长度与该节点的权的乘积。例如,图 1 中结点 b 的带权路径长度为 2 * 5 = 10
  5. 树的带权路径长度(WPL):树的带权路径长度为树中所有叶子节点的带权路径长度之和。下图中WPL为1*7+2*5+3*2+3*4=35。权值越大的节点离根节点越近的二叉树才是最优叉树

赫夫曼树
WPL最小的就是赫夫曼树
WPL最小的是35,所以蓝色的这颗树就是赫夫曼树

创建赫夫曼树思路

  1. 对数据进行从小到大排序,每一个数据都是一个节点,每个节点可以看成是一颗最简单的二叉树(只有根结点,没有左右子节点)
  2. 取出根节点权值最小的两颗二叉树(数据中前两个数)
  3. 用这两棵树组成一个新的二叉树,该新的二叉树的根节点的权值是这两颗二叉树根节点的权值的和
  4. 再将这颗新的二叉树,以根节点的权值大小和剩下的数据再次排序
  5. 不断的重复1,2,3,4的步骤,直到数列中,所有数据都被处理,就得到一颗赫夫曼树

    创建赫夫曼树图解

  6. 现在有一个数组34,62,11,5,47,每个数据都可以看作是一个节点,对这些数据进行排序

image.png

  1. 取出根节点的前两棵树,组成一颗新的二叉树,新的二叉树的根节点的权值是前两棵树权值的和。然后再将这颗二叉树根节点的权值进行从新排序,16正好排在34前面

image.png

  1. 16和34又组成一颗新的二叉树,新根节点的权值为50,50在47和62之间

image.png

  1. 47和50又组成一颗新的二叉树,新的根节点为97,97在62后面

image.png

  1. 62和97又组成一颗新的二叉树,这次全部数据都使用了,所以这就是最终的赫夫曼树
  2. image.png

    创建赫夫曼树代码

    ```csharp using System; using System.Collections.Generic;

namespace ConsoleApp1 { class Program {

  1. static void Main(string[] args)
  2. {
  3. int[] arr = { 13,7,8,3,29,6,1};
  4. HFNode hfnode = CreateHuffmanTree(arr);
  5. hfnode.preOrder();
  6. }
  7. //创建赫夫曼树
  8. public static HFNode CreateHuffmanTree(int[] arr)
  9. {
  10. //将数据变成节点装进list
  11. List<HFNode> hfnodes = new List<HFNode>();
  12. foreach (var a in arr)
  13. {
  14. hfnodes.Add(new HFNode(a));
  15. }
  16. //处理完的节点不断从列表中删除,最后当列表只有一个数据时,就说明处理完成了,赫夫曼树形成
  17. while (hfnodes.Count > 1)
  18. {
  19. //1.对节点集合进行权值从小到大排序
  20. hfnodes.Sort();
  21. //2.取出最小的两个节点,左右新二叉树的左右子节点
  22. HFNode leftnode = hfnodes[0];
  23. HFNode rightnode = hfnodes[1];
  24. //3.构建一颗新的二叉树,这个二叉树的根节点的权值等于左右子节点之和
  25. HFNode parent = new HFNode(leftnode.value + rightnode.value);
  26. //4.把这颗树的左右节点挂起来
  27. parent.left = leftnode;
  28. parent.right = rightnode;
  29. //5.从hfnodes中删除已经用过的节点
  30. hfnodes.Remove(leftnode);
  31. hfnodes.Remove(rightnode);
  32. //6.将parent加入hfnodes
  33. hfnodes.Add(parent);
  34. }
  35. //返回赫夫曼树的根节点
  36. return hfnodes[0];
  37. }
  38. /// <summary>
  39. /// 赫夫曼节点实现IComparable接口,作用是进行排序
  40. /// </summary>
  41. public class HFNode : IComparable<HFNode>
  42. {
  43. public int value;//节点的权值
  44. public HFNode left;
  45. public HFNode right;
  46. public HFNode(int value)
  47. {
  48. this.value = value;
  49. }
  50. //实现接口方法
  51. public int CompareTo(HFNode hfnode)
  52. {
  53. //从小到大排序
  54. return this.value - hfnode.value;
  55. }
  56. //前序遍历
  57. public void preOrder()
  58. {
  59. Console.WriteLine(this.value);
  60. if (this.left != null)
  61. this.left.preOrder();
  62. if (this.right != null)
  63. this.right.preOrder();
  64. }
  65. }
  66. }

}

```