哈夫曼问题


参考: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;
 }
}

哈夫曼编码

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值