【转载】只为让价值共享,如有侵权敬请见谅!

一、哈夫曼树的概念和定义

什么是哈夫曼树?

让我们先举一个例子。

判定树:

        在很多问题的处理过程中,需要进行大量的条件判断,这些判断结构的设计直接影响着程序的执行效率。例如,编制一个程序,将百分制转换成五个等级输出。大家可能认为这个程序很简单,并且很快就可以用下列形式编写出来:
 
  1. if(score<60)
  2. cout<<"Bad"<<endl;
  3. else if(score<70)
  4. cout<<"Pass"<<endl
  5. else if(score<80)
  6. cout<<"General"<<endl;
  7. else if(score<90)
  8. cout<<"Good"<<endl;
  9. else
  10. cout<<"Very good!"<<endl;

若考虑上述程序所耗费的时间,就会发现该程序的缺陷。在实际中,学生成绩在五个等级上的分布是不均匀的。当学生百分制成绩的录入量很大时,上述判定过程需要反复调用,此时程序的执行效率将成为一个严重问题。
 

但在实际应用中,往往各个分数段的分布并不是均匀的。下面就是在一次考试中某门课程的各分数段的分布情况:

 
 
下面我们就利用哈夫曼树寻找一棵最佳判定树,即总的比较次数最少的判定树。
 
 
第一种构造方式:
 
 
 
 
第二种构造方式:
 
 
 
 
 
这两种方式,显然后者的判定过程的效率要比前者高。在也没有别地判定过程比第二种方式的效率更高。
 
我们称判定过程最优的二叉树为哈夫曼树,又称最优二叉树
 
 
===================================================================================================
 

定义哈夫曼树之前先说明几个与哈夫曼树有关的概念:

路径: 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。

路径长度:路径上的分枝数目称作路径长度。

树的路径长度:从树根到每一个结点的路径长度之和。

结点的带权路径长度:在一棵树中,如果其结点上附带有一个权值,通常把该结点的路径长度与该结点上的权值

之积称为该结点的带权路径长度(weighted path length)

什么是权值?( From 百度百科 )

 计算机领域中(数据结构

 

  权值就是定义的路径上面的值。可以这样理解为节点间的距离。通常指字符对应的二进制编码出现的概率。

 

  至于霍夫曼树中的权值可以理解为:权值大表明出现概率大!

 

  一个结点的权值实际上就是这个结点子树在整个树中所占的比例.

 

  abcd四个叶子结点的权值为7,5,2,4. 这个7,5,2,4是根据实际情况得到的,比如说从一段文本中统计出abcd四个字母出现的次数分别为7,5,2,4. 说a结点的权值为7,意思是说a结点在系统中占有7这个份量.实际上也可以化为百分比来表示,但反而麻烦,实际上是一样的.

树的带权路径长度:如果树中每个叶子上都带有一个权值,则把树中所有叶子的带权路径长度之和称为树的带

权路径长度。

设某二叉树有n个带权值的叶子结点,则该二叉树的带权路径长度记为:

公式中,Wk为第k个叶子结点的权值;Lk为该结点的路径长度。

示例:

======================================================================================================
 
一般来说,用n(n>0)个带权值的叶子来构造二叉树,限定二叉树中除了这n个叶子外只能出现度为2的结点。
 
那么符合这样条件的二叉树往往可构造出许多颗,
 
 
其中带权路径长度最小的二叉树就称为哈夫曼树或最优二叉树
 
===============================================================================
 
 
 
 

二、哈夫曼树的构造

 
根据哈弗曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点
越远离根结点。
 
哈弗曼依据这一特点提出了一种构造最优二叉树的方法,其基本思想如下:
 
下面演示了用Huffman算法构造一棵Huffman树的过程:
 
 
 

三、哈夫曼树的在编码中的应用

在电文传输中,需要将电文中出现的每个字符进行二进制编码。在设计编码时需要遵守两个原则:
(1)发送方传输的二进制编码,到接收方解码后必须具有唯一性,即解码结果与发送方发送的电文完全一样;
(2)发送的二进制编码尽可能地短。下面我们介绍两种编码的方式。
 
1. 等长编码
            这种编码方式的特点是每个字符的编码长度相同(编码长度就是每个编码所含的二进制位数)。假设字符集只含有4个字符A,B,C,D,用二进制两位表示的编码分别为00,01,10,11。若现在有一段电文为:ABACCDA,则应发送二进制序列:00010010101100,总长度为14位。当接收方接收到这段电文后,将按两位一段进行译码。这种编码的特点是译码简单且具有唯一性,但编码长度并不是最短的。
 
2. 不等长编码
            在传送电文时,为了使其二进制位数尽可能地少,可以将每个字符的编码设计为不等长的,使用频度较高的字符分配一个相对比较短的编码,使用频度较低的字符分配一个比较长的编码。例如,可以为A,B,C,D四个字符分别分配0,00,1,01,并可将上述电文用二进制序列:000011010发送,其长度只有9个二进制位,但随之带来了一个问题,接收方接到这段电文后无法进行译码,因为无法断定前面4个0是4个A,1个B、2个A,还是2个B,即译码不唯一,因此这种编码方法不可使用。
 
因此,为了设计长短不等的编码,以便减少电文的总长,还必须考虑编码的唯一性,即在建立不等长编码时必须使任何一个字符的编码都不是另一个字符的前缀,这宗编码称为前缀编码(prefix  code)
 
 
 
(1)利用字符集中每个字符的使用频率作为权值构造一个哈夫曼树;
(2)从根结点开始,为到每个叶子结点路径上的左分支赋予0,右分支赋予1,并从根到叶子方向形成该叶子结点的编码
 
 
例题:
假设一个文本文件TFile中只包含7个字符{A,B,C,D,E,F,G},这7个字符在文本中出现的次数为{5,24,7,17,34,5,13}
利用哈夫曼树可以为文件TFile构造出符合前缀编码要求的不等长编码
 
具体做法:
 
1. 将TFile中7个字符都作为叶子结点,每个字符出现次数作为该叶子结点的权值
2. 规定哈夫曼树中所有左分支表示字符0,所有右分支表示字符1,将依次从根结点到每个叶子结点所经过的分支的二进制位的序列作为该
     结点对应的字符编码
3. 由于从根结点到任何一个叶子结点都不可能经过其他叶子,这种编码一定是前缀编码,哈夫曼树的带权路径长度正好是文件TFile编码
    的总长度
 
通过哈夫曼树来构造的编码称为哈弗曼编码(huffman code)
 
 
 
 
 
 

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. #define N 10         // 带编码字符的个数,即树中叶结点的最大个数
  6. #define M (2*N-1)    // 树中总的结点数目
  7. class HTNode{        // 树中结点的结构
  8. public:
  9. unsigned int weight;
  10. unsigned int parent,lchild,rchild;
  11. };
  12. class HTCode{
  13. public:
  14. char data;      // 待编码的字符
  15. int weight;     // 字符的权值
  16. char code[N];   // 字符的编码
  17. };
  18. void Init(HTCode hc[], int *n){
  19. // 初始化,读入待编码字符的个数n,从键盘输入n个字符和n个权值
  20. int i;
  21. printf("input n = ");
  22. scanf("%d",&(*n));
  23. printf("\ninput %d character\n",*n);
  24. fflush(stdin);
  25. for(i=1; i<=*n; ++i)
  26. scanf("%c",&hc[i].data);
  27. printf("\ninput %d weight\n",*n);
  28. for(i=1; i<=*n; ++i)
  29. scanf("%d",&(hc[i].weight) );
  30. fflush(stdin);
  31. }//
  32. void Select(HTNode ht[], int k, int *s1, int *s2){
  33. // ht[1...k]中选择parent为0,并且weight最小的两个结点,其序号由指针变量s1,s2指示
  34. int i;
  35. for(i=1; i<=k && ht[i].parent != 0; ++i){
  36. ; ;
  37. }
  38. *s1 = i;
  39. for(i=1; i<=k; ++i){
  40. if(ht[i].parent==0 && ht[i].weight<ht[*s1].weight)
  41. *s1 = i;
  42. }
  43. for(i=1; i<=k; ++i){
  44. if(ht[i].parent==0 && i!=*s1)
  45. break;
  46. }
  47. *s2 = i;
  48. for(i=1; i<=k; ++i){
  49. if(ht[i].parent==0 && i!=*s1 && ht[i].weight<ht[*s2].weight)
  50. *s2 = i;
  51. }
  52. }
  53. void HuffmanCoding(HTNode ht[],HTCode hc[],int n){
  54. // 构造Huffman树ht,并求出n个字符的编码
  55. char cd[N];
  56. int i,j,m,c,f,s1,s2,start;
  57. m = 2*n-1;
  58. for(i=1; i<=m; ++i){
  59. if(i <= n)
  60. ht[i].weight = hc[i].weight;
  61. else
  62. ht[i].parent = 0;
  63. ht[i].parent = ht[i].lchild = ht[i].rchild = 0;
  64. }
  65. for(i=n+1; i<=m; ++i){
  66. Select(ht, i-1, &s1, &s2);
  67. ht[s1].parent = i;
  68. ht[s2].parent = i;
  69. ht[i].lchild = s1;
  70. ht[i].rchild = s2;
  71. ht[i].weight = ht[s1].weight+ht[s2].weight;
  72. }
  73. cd[n-1] = '\0';
  74. for(i=1; i<=n; ++i){
  75. start = n-1;
  76. for(c=i,f=ht[i].parent; f; c=f,f=ht[f].parent){
  77. if(ht[f].lchild == c)
  78. cd[--start] = '0';
  79. else
  80. cd[--start] = '1';
  81. }
  82. strcpy(hc[i].code, &cd[start]);
  83. }
  84. }
  85. int main()
  86. {
  87. int i,m,n,w[N+1];
  88. HTNode ht[M+1];
  89. HTCode hc[N+1];
  90. Init(hc, &n);     // 初始化
  91. HuffmanCoding(ht,hc,n);   // 构造Huffman树,并形成字符的编码
  92. for(i=1; i<=n; ++i)
  93. printf("\n%c---%s",hc[i].data,hc[i].code);
  94. printf("\n");
  95. return 0;
  96. }  uffman

哈夫曼树【最优二叉树】【Huffman】的更多相关文章

  1. [C++]哈夫曼树(最优满二叉树) / 哈夫曼编码(贪心算法)

    一 哈夫曼树 1.1 基本概念 算法思想 贪心算法(以局部最优,谋求全局最优) 适用范围 1 [(约束)可行]:它必须满足问题的约束 2 [局部最优]它是当前步骤中所有可行选择中最佳的局部选择 3 [ ...

  2. 树形结构_红黑树:平衡2X 哈夫曼树:最优2X

    红黑树:平衡2X 哈夫曼树:最优2X 红黑树 :TreeSet.TreeMap 哈夫曼树 1. 将w1.w2.…,wn看成是有n 棵树的森林(每棵树仅有一个结点): 2. 在森林中选出根结点的权值最小 ...

  3. 霍夫曼(最优二叉树)和Java达到

    一.定义 一些定义: 节点之间的路径长度:在从节点树中的一个节点也经历分公司,这构成的两个节点之间的路径分支的数目后这就是所谓的路径长度 的路径长度:从树的根节点到树中每一结点的路径长度之和. 在结点 ...

  4. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  5. 哈夫曼树(三)之 Java详解

    前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:htt ...

  6. 哈夫曼树(二)之 C++详解

    上一章介绍了哈夫曼树的基本概念,并通过C语言实现了哈夫曼树.本章是哈夫曼树的C++实现. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载 ...

  7. 哈夫曼树(一)之 C语言详解

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  8. 20172332 2017-2018-2 《程序设计与数据结构》Java哈夫曼编码实验--哈夫曼树的建立,编码与解码

    20172332 2017-2018-2 <程序设计与数据结构>Java哈夫曼编码实验--哈夫曼树的建立,编码与解码 哈夫曼树 1.路径和路径长度 在一棵树中,从一个结点往下可以达到的孩子 ...

  9. 数据结构-二叉树(6)哈夫曼树(Huffman树)/最优二叉树

    树的路径长度是从树根到每一个结点的路径长度(经过的边数)之和. n个结点的一般二叉树,为完全二叉树时取最小路径长度PL=0+1+1+2+2+2+2+… 带权路径长度=根结点到任意结点的路径长度*该结点 ...

随机推荐

  1. 第三方ProgressHUD进度条 技术分享

    创建一个进度辅助视图: 初始化: - (void)viewDidLoad { [super viewDidLoad]; <span style="color: rgb(255, 0, ...

  2. go-mysql: database/sql 接口适配

    go-mysql已经支持golang database/sql接口,并通过https://github.com/bradfitz/go-sql-test测试用例. 现在go-mysql可以直接通过go ...

  3. Touch Handling in Cocos2D 3.x(六)

    使英雄变成可触碰的对象 这是另一个非常有用的特性.很多用户需要捡起已经存在的英雄然后满屏幕移动它们.让我们按以下步骤实现该功能: 如果用户触摸屏幕空白位置,一个新的英雄将被创建 如果用户触摸一个已经存 ...

  4. H5学习之旅-H5的新特性(1)

    随着H5的发展,网页开发呈现了跨平台的特性,目前H5是兼容各大平台的,这也为一些其他的技术的跨平台开发提供了基础,所以我预测后H5会成为很重要的开发语言 新特性介绍 1.用于回话的canvas标签 2 ...

  5. Python学习笔记 - function调用和定义

    调用函数: #!/usr/bin/env python3 # -*- coding: utf-8 -*- # 函数调用 >>> abs(100) 100 >>> a ...

  6. [易飞]设置导入导出规则-小BUG

    易飞系统在系统设置中-有设置导入导出规则,进行数据导入导出. 测试一:导入录入交易对象.从A账套导出到B账套,OK没有问题. 测试二:设置采购单单据性质. 导出结果: 怎么回事?把所有单据性质都导出了 ...

  7. java中,用json格式转换遇到问题

    将list转为JSONObject类,报 org/apache/commons/lang/exception/NestableRuntimeException是什么原因? 还需要导入这些包common ...

  8. android 加载图片oom若干方案小结

    本文根据网上提供的一些技术方案加上自己实际开发中遇到的情况小结. 众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视手机而定).一般我们可以通过获取当 ...

  9. 【一天一道LeetCode】#28. Implement strStr()

    一天一道LeetCode系列 (一)题目 Implement strStr(). Returns the index of the first occurrence of needle in hays ...

  10. Unity3D学习笔记(四)Unity的网络基础(C#)

    一 网络下载可以使用WWW类下载资源用法:以下载图片为例WWW date = new WWW("<url>");yield return date;texture = ...