学习一样事物,自然要先明其义,再通其用。哈夫曼树,顾名思义是一种树,不过它是一类带权路径最短的树。何谓权值呢?权值就是定义的路径上面的值。可以这样理解为结点间的距离;它通常指字符对应的二进制编码出现的概率。至于哈夫曼树中的权值可以理解为:权值大表明出现概率大!一个结点的权值实际上就是这个结点子树在整个树中所占的比例.
举一个网上给的例子:
abcd四个叶子节点的权值为7,5,2,4. 这个7,5,2,4是根据实际情况得到的,比如说从一段文本中统计出abcd四个字母出现的次数分别为7,5,2,4. 说a结点的权值为7,意思是说a结点在系统中占有7这个份量.实际上也可以化为百分比来表示,但反而麻烦,实际上是一样的.
由此哈夫曼树叶称作最优树或二叉树,不过这里要注意完全二叉树不一定就是最优二叉树呦!!那么哈夫曼树怎样构造呢?它的构造方式如下:
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

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

嗯,现在哈夫曼树的定义以及构造方式已经描述完了,就差通过代码把这个过程实现。通过两部曲,我们把哈夫曼树构造理解清楚!
在此之前,你会不会有疑问:为什么这样构造就能确保带权路径最小呢???我们在高中接触过排序不等式公式,如下:
0<a1<a2<a3......<an 0<b1<b2<b3......<bn
然后有:an×bn+an-1×bn-1+......+a1×b1>=乱序和>=a1×bn+a2×bn-1+......+an×b1
构建哈夫曼树的方法使权值越大的节点离根节点越近,而权值越小的节点离根节点越远,如此便能保证带权路径最小了!
第一曲:手工构造!!
手工构造,其实非常简单,就是自己创建几个节点,然后建立节点之间的关系,形成一棵树,左节点上的权值为0,右节点未1,父节点也为1,最后把叶子节点的编码打印出来。这个过程实际上就是帮助我们更好地理解哈夫曼树的构造过程,为自动建树巩固基础!
第二曲:自动建树!!
通过给定一个字符串,然后统计相同字符出现的次数,再根据这些次数自动创建哈夫曼树,再输出叶子节点的编码。
代码如下:
public class HFMTreeCode { class Node{ private Node left;//左节点 private Node right;//右节点 private Node parent=null;//父节点 private int data;//数据 } private Node root;//根节点 //哈夫曼树的手工建造 public Node TreeCreat(){ // 19 // 8 11 // 4 4 5 6 // 2 2 Node n1=new Node(); n1.data=2; Node n2=new Node(); n2.data=2; Node n3=new Node(); n3.data=n1.data+n2.data; n3.left=n1; n3.right=n2; Node n4=new Node(); n4.data=4; Node n5=new Node(); n5.data=5; Node n6 =new Node(); n6.data=6; Node n7=new Node(); n7.data=n3.data+n4.data; n7.left=n3; n7.right=n4; Node n8=new Node(); n8.data=n5.data+n6.data; n8.left=n5; n8.right=n6; Node n9=new Node(); n9.data=n7.data+n8.data; n9.left=n7; n9.right=n8; root=n9; return root; } //输出哈夫曼树的编码 public void Treeprint(int []data) { Node []nodes=new Node[data.length];//创建一个节点类型的数组 //遍历数组,将data数组里的数据传给nodes数组里data数据 for(int i=0;i<nodes.length;i++) { nodes[i]= new Node(); nodes[i].data=data[i]; } while(nodes.length>1) { //排序 BubbleSort(nodes); //找到最小的两个值,然后生成新结点 Node node1=new Node(); Node node2=new Node(); node1=nodes[0]; node2=nodes[1]; Node newNode=new Node(); newNode.left=node1; newNode.right=node2; newNode.data=node1.data+node2.data;//新结点数据之值为nodes数组两个含最小data数值的节点它们data值之和 //创建一个新Node类型数组,存放原来nodes数组中除两个含最小data值的node外其他node和新结点 Node[]node1s=new Node[nodes.length-1]; for(int i=2;i<nodes.length;i++) { node1s[i-2]=nodes[i]; } node1s[node1s.length-1]=newNode; nodes=node1s;//交换两个数组地址 } root=nodes[0]; TreePrint(root,""); } //打印当前节点的编码,用递归的思想 public void TreePrint(Node node ,String code){ if(node!=null){ if(node.left==null&&node.right==null) {//只打印出需要的节点编码 System.out.println(node.data+"编码是"+code); } TreePrint(node.left,code+"0");//递归调用打印左节点编码,每次都+0 TreePrint(node.right,code+"1");//递归调用打印右节点编码,每次都+1 } } /* * 冒泡法 */ public void BubbleSort(Node [] array) { //循环遍历数组 for(int i=0;i<array.length;i++) { for(int j=i+1;j<array.length;j++) { if(array[i].data>array[j].data) { Node temp = new Node(); temp=array[i]; array[i]=array[j]; array[j]=temp; } } } } /* * 交换数组元素的方法 */ public void swap(Node [] array,int x,int y) { Node temp=null;//设置一个中间变量,先初始化为0 //交换过程 temp=array[x]; array[x]=array[y]; array[y]=temp; } /** * @param args */ /* * 统计一个字符串中不同字符的次数,并返回一个存入次数的整型数组 */ public int[]arrayPrint(String str) { String str1="";//创建一个存储不含重复字符的字符串 for(int i=0;i<str.length();i++) { char c=str.charAt(i); if(str1.indexOf(c)==-1) {//如果str1中不含c str1+=c;//则将c加到str1上 } } int []array=new int[str1.length()]; //遍历数组,把字符出现的次数加到整型数组array[]中 for(int i=0;i<str1.length();i++) { char ch1=str1.charAt(i); array[i]=getCount(str,ch1); } for(int i=0;i<array.length;i++) { System.out.println(array[i]); } return array; } /* * 统计一个字符在字符串中出现的次数 */ public int getCount(String str,char ch) { int count=0; for(int i=0;i<str.length();i++) { if(str.charAt(i)==ch) count++; } return count; } public static void main(String[] args) { // TODO Auto-generated method stub //int[]array={1,4,5,8,2}; String str="dfsdfdsfsdfsdfsdasdas"; HFMTreeCode htc=new HFMTreeCode(); htc.Treeprint(htc.arrayPrint(str)); } }说明下,这里不需要另外创建一个结点类,而是直接在这个类内部中创建,因为结点类一般不会被其他类所使用,它的创建单纯是为了这个类的实现。
接下来,在完成了这些后,我们就可以开始朝哈夫曼压缩方面努力了!!!
相关推荐
哈夫曼树和哈夫曼编码是数据压缩领域中的重要技术。哈夫曼树是带权路径长度最短的二叉树,它基于字符出现频率的不同构建一棵最优二叉树,从而实现数据的无损压缩。哈夫曼编码是一种变长编码方法,是哈夫曼树的具体...
内容概要:文章介绍了哈夫曼树的概念及其构建方法,具体分为初始化、选择最小权值的两棵树合并以及更新森林三个主要阶段,直至森林合并为单棵哈夫曼树。随后讨论了哈夫曼树的重要应用场景之一——哈夫曼编码。在此...
1. 将提供的字符串(自定义字符串)进行排序,获取...5. 直到链表中只剩一个节点时,将此节点赋给哈夫曼树头; 6. 利用创建的哈夫曼树得到编码; 用递归得到叶子节点,由叶子节点追溯到根节点,得到编码后反转顺序;
数据结构——哈夫曼树——实验
在IT领域,哈夫曼编码是一种广泛应用于数据压缩的高效算法,它基于字符频率构建最优的二叉树,即哈夫曼树。本课程聚焦于数据结构实践,特别是利用哈夫曼树进行文件的压缩与解压,同时结合了Microsoft Foundation ...
2. **创建哈夫曼树**:根据字符频率创建最小堆,然后逐步合并两个最小节点直到只剩一个节点,即为哈夫曼树。 3. **编码生成**:遍历哈夫曼树,构建字符到编码的映射表。 4. **编码输出**:读取输入数据,利用...
"数据结构哈夫曼树PPT学习教案.pptx" 哈夫曼树是一种特殊的二叉树,它的带权路径长度最小。哈夫曼树的构造是数据结构中的一种重要算法,广泛应用于数据压缩、编码和决策树等领域。 一、基本术语 在哈夫曼树中,...
- 重复这个过程,每次都将两个最小权值的树合并,直到森林只剩下一棵树,即为哈夫曼树。 - 为了保证唯一性,通常规定新树的左子树权重小于等于右子树权重。 3. **算法实现**: - 常用的存储结构是双亲孩子表示法...
哈夫曼树哈夫曼树项目代码-哈夫曼树项目代码
哈夫曼树的构建过程通常通过合并频率最低的两个节点(称为“贪心”策略)不断进行,直到所有字符形成一棵树,每个字符最终成为一个叶子节点。 2. 编码:利用构建好的哈夫曼树,从叶子节点到根节点的路径表示该字符...
- **构建过程**:创建哈夫曼树的过程通常是基于一组初始权重值,将这些值看作是单个节点的权重,并不断合并最小的两个节点,直到构造出整棵树。 - **代码实现**:虽然给定的代码片段没有具体展示哈夫曼树的创建过程...
### 数据结构哈夫曼树课程设计相关知识点 ...通过对哈夫曼树及其应用的学习和实践,不仅加深了对数据结构的理解,还提高了编程能力和问题解决能力。此外,实际项目的经验积累对于未来的职业发展也极为有益。
1. **Select 函数**:该函数用于从哈夫曼树 HT 的当前节点集合中选择两个具有最小权重的节点。 - 参数说明: - `HuffmanTree &HT`: 哈夫曼树引用。 - `int n`: 当前节点集合中的节点数量。 - `int &s1`: 返回的...
通常通过哈夫曼构建算法实现,该算法将频率最低的两个节点合并为一个新的节点,新节点的频率是其子节点频率之和,重复此过程直到只剩下一个节点,即为哈夫曼树的根节点。 3. **生成哈夫曼编码**:从哈夫曼树中生成...
自用,作业,可用
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int num)//w存放n个字符的权值(均>0),构造哈夫曼树HT,并求出n个字符的哈弗曼编码HC { int i,m,c,s1,s2,start,f; HuffmanTree p; char* cd; if...
哈夫曼编码的过程包括构建哈夫曼树和生成编码表两部分: 1. 构建哈夫曼树后,从根节点到每个叶子节点的路径可以看作是该叶子节点的哈夫曼编码,通常约定向左走代表0,向右走代表1。 2. 生成编码表,将每个字符的编码...
哈夫曼树的构建是通过选择权值最小的两个结点,合并成一个新的结点,直到所有结点都合并成一个根结点为止。 哈夫曼树的应用 哈夫曼树的应用非常广泛,包括数据压缩、编码、解码、数据传输等领域。哈夫曼树可以用来...
2. **合并最小权值树**:每次从森林中选取两个权值最小的树合并,创建一个新的树,新树的权值是两棵小树的权值之和,新树的左右子树分别为这两棵小树。 3. **删除旧树,添加新树**:将合并后的树添加回森林,同时...
实验题目涉及了哈夫曼编码与译码的基本原理和实现,旨在让学习者掌握哈夫曼树的构建,理解哈夫曼编码的生成方法,并能实现对电文的高效编码与解码。哈夫曼编码是一种最优的前缀编码方式,它通过构建最小带权路径长度...