当前位置:   article > 正文

哈夫曼问题_哈夫曼树比2个加起来的大

哈夫曼树比2个加起来的大


参考:https://blog.csdn.net/qq_29519041/article/details/81428934
http://c.biancheng.net/view/3398.html

什么是哈夫曼树

哈夫曼树,即最优二叉树,是一类带权路径长度最短的树
所谓树的带权路径长度,就是树中所有的叶节点的权值,乘上其到根节点的路径长度(若根节点为0层,页结点到根节点的路径长度为叶节点的层数)。整个树的带权路径长度是从树根到每一结点的带权路径长度之和。

怎么理解这句话:以下图为例:
在这里插入图片描述
上面这颗二叉树的叶节点为a,b,c,d, 则各个叶节点的路径长度分别为:
a->1;b->2;c->3;d->3;
且叶结点a的带权路径长度为 1x1;叶节点b的带权路径长度为3x2;叶节点c的带权路径长度为2x3;叶节点d的带权路径长度为5x3;
那么整颗数的带权路径长度为1x1+3x2+2x3+5x3 = 28
那么什么是哈夫曼树呢? 就是当n个带权的叶节点试图构建一棵树时,所构建的所有树中带权路径长度最小的那个树就是哈夫曼树。

如何实现哈夫曼树

构建哈夫曼树,就要满足权值较高的那个离根节点更近的原则。
具体的构建流程大致如下:
假设将n个带权叶节点进行构建,如:A->31;B->62;c->26;D->12;E->42;F->34;
1:首先,从这n个结点中选两个权值最小的结点,将这两个结点作为一个新结点的孩子结点。小的在右,大的在左。这个新结点的权值为这两个结点权值之和。然后将这两个结点从这n个结点中删除。将新结点放到其余结点集合中。
那么剩下的结点为:A->31;B->62;E->42;F->34;DC->38
构建的二叉树为:
在这里插入图片描述
重复1过程,则剩下的结点为:B->62;E->42;DC->38;AF->65;
构建的树形结构为:
在这里插入图片描述
继续重复1过程,则剩下的结点为:B->62;AF->65;DCE->80;
构建的树形结构为:
在这里插入图片描述
继续重复1过程,则剩下的结点为:BAF>127;DCE->80;
构建的树形结构为:
在这里插入图片描述
重复1过程,则剩下的结点为:DCEBAF->207,构建的树形结构为
在这里插入图片描述
那么DCEBAF结点就位根节点。并且此时构建 的树就为哈夫曼树。

哈夫曼树的代码实现:

//首先要使用一个结构来作为哈夫曼树的结点,由哈夫曼树的构建过程我们可以知道,这个结点包含:权重,父节点,左子节点,右子节点
typedef struct 
{
 int weight;//结点权重
 int parent, left, right;//父结点、左孩子、右孩子在数组中的位置下标
}HTNode, *HuffmanTree;//定义结点以及结点数组
//然后我们要构建一个函数来得到n个结点中最小的两个结点,其思想为;
//从结点起始位置开始,首先找到两个无父结点的结点(说明还未使用其构建成树),
//然后和后续无父结点的结点依次做比较,有两种情况需要考虑:
//如果比两个结点中较小的那个还小,就保留这个结点,删除原来较大的结点;
//如果介于两个结点权重值之间,替换原来较大的结点;
//函数实现为:
//HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的下标
void Select(HuffmanTree HT, int end, int &s1, int &s2)
{
 int min1, min2;//两个结点的权重,
 //遍历数组初始下标为 1
 int i = 1;
 //找到还没构建树的结点
 while (HT[i].parent != 0 && i <= end)
 {
  i++;
 }//一直遍历到当HT中的结点没有父节点时(parent==0),且保证i不到达没有存储结点的位置
 min1 = HT[i].weight;//先找到其中一个叶节点,将权值赋值给min1
 s1 = i;
 i++;//i+1表示找下一个叶节点
 while (HT[i].parent != 0 && i <= end)
 {
  i++;
 }//此时的i为下一个叶节点的下标
 //对找到的两个结点比较大小,让min2为大的,min1为小的
 if (HT[i].weight < min1)
 {
  min2 = min1;
  s2 = s1;
  min1 = HT[i].weight;
  s1 = i;
 }
 else
 {
  min2 = HT[i].weight;
  s2 = i;
 }
 //两个结点和后续的所有未构建成树的结点做比较 
 for (int j = i + 1; j <= end; j++)
 {
  //如果有父结点,直接跳过,进行下一个
  if (HT[j].parent != 0)
  {
   continue;
  }
  //如果比最小的还小,将min2=min1,min1赋值新的结点的下标
  if (HT[j].weight < min1)
  {
   min2 = min1;
   min1 = HT[j].weight;
   s2 = s1;
   s1 = j;
  }
  //如果介于两者之间,min2赋值为新的结点的位置下标
  else if (HT[j].weight >= min1 && HT[j].weight < min2)
  {
   min2 = HT[j].weight;
   s2 = j;
  }
 }
}
//接下来创建哈夫曼树
//HT为哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
void CreateHuffmanTree(HuffmanTree &HT, int *w, int n)
{
 if (n <= 1) return; // 如果只有一个编码就相当于0
 int m = 2 * n - 1; // 哈夫曼树总节点数,n就是叶子结点
 HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode)); //因为我们要让parent的下标为0,所以0号位置不用
 HuffmanTree p = HT;
 // 初始化哈夫曼树中的所有叶子结点
 for (int i = 1; i <= n; i++)
 {
  (p + i)->weight = *(w + i - 1);
  (p + i)->parent = 0;
  (p + i)->left = 0;
  (p + i)->right = 0;
 }
 //从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点
 for (int i = n + 1; i <= m; i++)
 {
  (p + i)->weight = 0;
  (p + i)->parent = 0;
  (p + i)->left = 0;
  (p + i)->right = 0;
 }
 //构建哈夫曼树
 //构建哈夫曼树
 for (int i = n + 1; i <= m; i++)//每构建一次都要产生一个新结点
 {
  int s1, s2;
  Select(HT, i - 1, s1, s2);//得到最下结点的下标
  (HT)[s1].parent = (HT)[s2].parent = i;//将这两个结点的父节点共同表示为新结点
  //新结点的左右孩子及新权重
  (HT)[i].left = s1;
  (HT)[i].right = s2;
  (HT)[i].weight = (HT)[s1].weight + (HT)[s2].weight;
 }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105

哈夫曼编码

在这里插入图片描述
如上图,将0放左边,1放右边,则ABCDEF的编码为:
A:110
B:10
C:001
D:000
E:01
F:111

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/227437
推荐阅读
相关标签
  

闽ICP备14008679号