1、基本概念

哈夫曼树又称最优二叉树。它是 n 个带权叶子结点构成的所有二叉树中,带权路径长度 WPL 最小的二叉树。

如下图为一哈夫曼树示意图。

2、构造哈夫曼树

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

  • (1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
  • (2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
  • (3)从森林中删除选取的两棵树,并将新树加入森林;
  • (4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:

注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权,具体算法如下:

//2、根据数组 a 中 n 个权值建立一棵哈夫曼树,返回树根指针
struct BTreeNode* CreateHuffman(ElemType a[], int n)
{  int i, j;  struct BTreeNode **b, *q;  b = malloc(n*sizeof(struct BTreeNode));  for (i = 0; i < n; i++) //初始化b指针数组,使每个指针元素指向a数组中对应的元素结点  {  b[i] = malloc(sizeof(struct BTreeNode));  b[i]->data = a[i];  b[i]->left = b[i]->right = NULL;  }  for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树  {  //k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标  int k1 = -1, k2;  for (j = 0; j < n; j++)//让k1初始指向森林中第一棵树,k2指向第二棵  {  if (b[j] != NULL && k1 == -1)  {  k1 = j;  continue;  }  if (b[j] != NULL)  {  k2 = j;  break;  }  }  for (j = k2; j < n; j++)//从当前森林中求出最小权值树和次最小  {  if (b[j] != NULL)  {  if (b[j]->data < b[k1]->data)  {  k2 = k1;  k1 = j;  }  else if (b[j]->data < b[k2]->data)  k2 = j;  }  }  //由最小权值树和次最小权值树建立一棵新树,q指向树根结点  q = malloc(sizeof(struct BTreeNode));  q->data = b[k1]->data + b[k2]->data;  q->left = b[k1];  q->right = b[k2];  b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置  b[k2] = NULL;//k2位置为空  }  free(b); //删除动态建立的数组b  return q; //返回整个哈夫曼树的树根指针
}  

哈夫曼编码

在电报通信中,电文是以二进制的0、1序列传送的,每个字符对应一个二进制编码,为了缩短电文的总长度,采用不等长编码方式,构造哈夫曼树。

将每个字符的出现频率作为字符结点的权值赋予叶子结点,每个分支结点的左右分支分别用0和1编码,从树根结点到每个叶子结点的路径上。所经分支的0、1编码序列等于该叶子结点的二进制编码。如上文所示的哈夫曼编码如下:

a 的编码为:00

b 的编码为:01

c 的编码为:100

d 的编码为:1010

e 的编码为:1011

f 的编码为:11

哈夫曼树的操作运算

以上文的哈夫曼树作为具体实例,用详细的程序展示哈夫曼树的操作运算

#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
struct BTreeNode
{  ElemType data;  struct BTreeNode* left;  struct BTreeNode* right;
};  //1、输出二叉树,可在前序遍历的基础上修改。采用广义表格式,元素类型为int
void PrintBTree_int(struct BTreeNode* BT)
{  if (BT != NULL)  {  printf("%d", BT->data); //输出根结点的值  if (BT->left != NULL || BT->right != NULL)  {  printf("(");  PrintBTree_int(BT->left); //输出左子树  if (BT->right != NULL)  printf(",");  PrintBTree_int(BT->right); //输出右子树  printf(")");  }  }
}  //2、根据数组 a 中 n 个权值建立一棵哈夫曼树,返回树根指针
struct BTreeNode* CreateHuffman(ElemType a[], int n)
{  int i, j;  struct BTreeNode **b, *q;  b = malloc(n*sizeof(struct BTreeNode));  for (i = 0; i < n; i++) //初始化b指针数组,使每个指针元素指向a数组中对应的元素结点  {  b[i] = malloc(sizeof(struct BTreeNode));  b[i]->data = a[i];  b[i]->left = b[i]->right = NULL;  }  for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树  {  //k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标  int k1 = -1, k2;  for (j = 0; j < n; j++)//让k1初始指向森林中第一棵树,k2指向第二棵  {  if (b[j] != NULL && k1 == -1)  {  k1 = j;  continue;  }  if (b[j] != NULL)  {  k2 = j;  break;  }  }  for (j = k2; j < n; j++)//从当前森林中求出最小权值树和次最小  {  if (b[j] != NULL)  {  if (b[j]->data < b[k1]->data)  {  k2 = k1;  k1 = j;  }  else if (b[j]->data < b[k2]->data)  k2 = j;  }  }  //由最小权值树和次最小权值树建立一棵新树,q指向树根结点  q = malloc(sizeof(struct BTreeNode));  q->data = b[k1]->data + b[k2]->data;  q->left = b[k1];  q->right = b[k2];  b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置  b[k2] = NULL;//k2位置为空  }  free(b); //删除动态建立的数组b  return q; //返回整个哈夫曼树的树根指针
}  //3、求哈夫曼树的带权路径长度
ElemType WeightPathLength(struct BTreeNode* FBT, int len)//len初始为0
{  if (FBT == NULL) //空树返回0  return 0;  else  {  if (FBT->left == NULL && FBT->right == NULL)//访问到叶子结点  return FBT->data * len;  else //访问到非叶子结点,进行递归调用,返回左右子树的带权路径长度之和,len递增  return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);  }
}  //4、哈夫曼编码(可以根据哈夫曼树带权路径长度的算法基础上进行修改)
void HuffManCoding(struct BTreeNode* FBT, int len)//len初始值为0
{  static int a[10];//定义静态数组a,保存每个叶子的编码,数组长度至少是树深度减一  if (FBT != NULL)//访问到叶子结点时输出其保存在数组a中的0和1序列编码  {  if (FBT->left == NULL && FBT->right == NULL)  {  int i;  printf("结点权值为%d的编码:", FBT->data);  for (i = 0; i < len; i++)  printf("%d", a[i]);  printf("\n");  }  else//访问到非叶子结点时分别向左右子树递归调用,并把分支上的0、1编码保存到数组a  {   //的对应元素中,向下深入一层时len值增1  a[len] = 0;  HuffManCoding(FBT->left, len + 1);  a[len] = 1;  HuffManCoding(FBT->right, len + 1);  }  }
}  //主函数
void main()
{  int n, i;  ElemType* a;  struct BTreeNode* fbt;  printf("从键盘输入待构造的哈夫曼树中带权叶子结点数n:");  while(1)  {  scanf("%d", &n);  if (n > 1)  break;  else  printf("重输n值:");  }  a = malloc(n*sizeof(ElemType));  printf("从键盘输入%d个整数作为权值:", n);  for (i = 0; i < n; i++)  scanf(" %d", &a[i]);  fbt = CreateHuffman(a, n);  printf("广义表形式的哈夫曼树:");  PrintBTree_int(fbt);  printf("\n");  printf("哈夫曼树的带权路径长度:");  printf("%d\n", WeightPathLength(fbt, 0));  printf("树中每个叶子结点的哈夫曼编码:\n");  HuffManCoding(fbt, 0);
}  

运行结果:

「三分钟系列07」3分钟看懂哈夫曼树与哈夫曼编码相关推荐

  1. 「Python爬虫系列讲解」三、正则表达式爬虫之牛刀小试

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  2. 「Python爬虫系列讲解」七、基于数据库存储的 BeautifulSoup 招聘爬取

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  3. 「Python爬虫系列讲解」八、Selenium 技术

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  4. 「Python爬虫系列讲解」十二、基于图片爬取的 Selenium 爬虫

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  5. 「Python爬虫系列讲解」十四、基于开发者工具 Network 的数据抓包技术

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  6. 「Python爬虫系列讲解」十一、基于登录分析的 Selenium 微博爬虫

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  7. 「微服务系列 13」熔断限流隔离降级

    我们知道微服务分布式依赖关系错综复杂,比方说前端的请求转化为后端调用的服务请求,一个前端请求会转为成很多个后端调用的服务请求,那么这个时候后台的服务出现不稳定或者延迟,如果没有好的限流熔断措施,可能会 ...

  8. 【转帖】十分钟经济学--让你看懂中国经济形势

    十分钟经济学--让你看懂中国经济形势 本文以最浅显的道理阐述经济的真相.还事实以清白,真相只有一个,让我们擦亮眼睛,洞观今天发生的一切.本文将在以下几方面阐述中国经济的真相. 1. 什么是经济发展? ...

  9. 「Python爬虫系列讲解」二、Python知识初学

    本专栏是以杨秀璋老师爬虫著作<Python网络数据爬取及分析「从入门到精通」>为主线.个人学习理解为主要内容,以学习笔记形式编写的. 本专栏不光是自己的一个学习分享,也希望能给您普及一些关 ...

  10. 一分钟看懂mysql_冬天发财树“最怕”它,遇上就烂根,一分钟看懂就能养好

    冬天发财树"最怕"它,遇上就烂根,一分钟看懂就能养好 在平时养殖一些花花草草的时候,经常会遇到植物烂根黄叶的情况,有些花友把花卉盆栽买回家没有多久,植株已经出现了烂根.黄叶,最后剩 ...

最新文章

  1. 反射机制(java)
  2. oracle 取今日0时,Oracle 取得当天0时0分0秒和23时59分59秒
  3. kubesphere devops介绍、访问账密及nfs数据备份同步
  4. rust灯灭了怎么办_Rust Rc 方法整理
  5. JBoss模块很烂,无法在JBoss 7下使用自定义Resteasy / JAX-RS
  6. MTFBWU的完整形式是什么?
  7. Kerberos加密级别不支持的问题
  8. 飞龙的计算机公开课推荐
  9. linux 多块磁盘挂一个文件夹,Linux单目录挂载多块磁盘
  10. 洛谷 P2089 烤鸡
  11. POJ 2942Knights of the Round Table(二分图判定+双连通分量)
  12. 彻底删除VMware !!!
  13. netron安装使用
  14. 使用pgAdmin把Excel文件导入PostgreSql数据库
  15. [计算机组成原理] 第六章 控制系统与CPU
  16. 《软技能》读书笔记(上)
  17. 设置vlan虚拟局域网
  18. GMT绘图geotiff转grd
  19. 2012暑期川西旅游之第八天(青城山-成都)_我是亲民_新浪博客
  20. 【Ansoft Maxwell】Unable to locate or start COM engine on ‘Loacal Machine‘解决方案

热门文章

  1. 网站移植到linux上后常犯的错误
  2. 数据结构(一)之线性表
  3. sql中用临时表 或 创建视图那个效率比较快!
  4. [转]OAuth 认证步骤
  5. 从北京站到天通苑怎么走,
  6. java获取当前项目或类路径
  7. 【8.16校内测试】【队列】【数学】【网络流/二分图最大匹配】
  8. The freeze_support() line can be omitted
  9. GIS-python学习
  10. 2018 推荐系统总结