前面分别经过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本。html
目录
1. 哈夫曼树的介绍
2. 哈夫曼树的图文解析
3. 哈夫曼树的基本操做
4. 哈夫曼树的完整源码 java转载请注明出处:http://www.cnblogs.com/skywang12345/git
更多内容:数据结构与算法系列 目录github
Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树。算法
定义:给定n个权值做为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树。 这个定义里面涉及到了几个陌生的概念,下面就是一颗哈夫曼树,咱们来看图解答。数组
(01) 路径和路径长度数据结构
定义:在一棵树中,从一个结点往下能够达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
例子:100和80的路径长度是1,50和30的路径长度是2,20和10的路径长度是3。ide
(02) 结点的权及带权路径长度测试
定义:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
例子:节点20的路径长度是3,它的带权路径长度= 路径长度 * 权 = 3 * 20 = 60。ui
(03) 树的带权路径长度
定义:树的带权路径长度规定为全部叶子结点的带权路径长度之和,记为WPL。
例子:示例中,树的WPL= 1*100 + 2*80 + 3*20 + 3*10 = 100 + 160 + 60 + 30 = 350。
比较下面两棵树
上面的两棵树都是以{10, 20, 50, 100}为叶子节点的树。
左边的树WPL=2*10 + 2*20 + 2*50 + 2*100 = 360
右边的树WPL=350
左边的树WPL > 右边的树的WPL。你也能够计算除上面两种示例以外的状况,但实际上右边的树就是{10,20,50,100}对应的哈夫曼树。至此,应该堆哈夫曼树的概念有了必定的了解了,下面看看如何去构造一棵哈夫曼树。
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,哈夫曼树的构造规则为:
1. 将w1、w2、…,wn当作是有n 棵树的森林(每棵树仅有一个结点);
2. 在森林中选出根结点的权值最小的两棵树进行合并,做为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
3. 从森林中删除选取的两棵树,并将新树加入森林;
4. 重复(02)、(03)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
以{5,6,7,8,15}为例,来构造一棵哈夫曼树。
第1步:建立森林,森林包括5棵树,这5棵树的权值分别是5,6,7,8,15。
第2步:在森林中,选择根节点权值最小的两棵树(5和6)来进行合并,将它们做为一颗新树的左右孩子(谁左谁右可有可无,这里,咱们选择较小的做为左孩子),而且新树的权值是左右孩子的权值之和。即,新树的权值是11。 而后,将"树5"和"树6"从森林中删除,并将新的树(树11)添加到森林中。
第3步:在森林中,选择根节点权值最小的两棵树(7和8)来进行合并。获得的新树的权值是15。 而后,将"树7"和"树8"从森林中删除,并将新的树(树15)添加到森林中。
第4步:在森林中,选择根节点权值最小的两棵树(11和15)来进行合并。获得的新树的权值是26。 而后,将"树11"和"树15"从森林中删除,并将新的树(树26)添加到森林中。
第5步:在森林中,选择根节点权值最小的两棵树(15和26)来进行合并。获得的新树的权值是41。 而后,将"树15"和"树26"从森林中删除,并将新的树(树41)添加到森林中。
此时,森林中只有一棵树(树41)。这棵树就是咱们须要的哈夫曼树!
哈夫曼树的重点是如何构造哈夫曼树。本文构造哈夫曼时,用到了之前介绍过的"(二叉堆)最小堆"。下面对哈夫曼树进行讲解。
1. 基本定义
public class HuffmanNode implements Comparable, Cloneable { protected int key; // 权值 protected HuffmanNode left; // 左孩子 protected HuffmanNode right; // 右孩子 protected HuffmanNode parent; // 父结点 protected HuffmanNode(int key, HuffmanNode left, HuffmanNode right, HuffmanNode parent) { this.key = key; this.left = left; this.right = right; this.parent = parent; } @Override public Object clone() { Object obj=null; try { obj = (HuffmanNode)super.clone();//Object 中的clone()识别出你要复制的是哪个对象。 } catch(CloneNotSupportedException e) { System.out.println(e.toString()); } return obj; } @Override public int compareTo(Object obj) { return this.key - ((HuffmanNode)obj).key; } }
HuffmanNode是哈夫曼树的节点类。
public class Huffman { private HuffmanNode mRoot; // 根结点 ... }
Huffman是哈夫曼树对应的类,它包含了哈夫曼树的根节点和哈夫曼树的相关操做。
2. 构造哈夫曼树
/* * 建立Huffman树 * * @param 权值数组 */ public Huffman(int a[]) { HuffmanNode parent = null; MinHeap heap; // 创建数组a对应的最小堆 heap = new MinHeap(a); for(int i=0; i<a.length-1; i++) { HuffmanNode left = heap.dumpFromMinimum(); // 最小节点是左孩子 HuffmanNode right = heap.dumpFromMinimum(); // 其次才是右孩子 // 新建parent节点,左右孩子分别是left/right; // parent的大小是左右孩子之和 parent = new HuffmanNode(left.key+right.key, left, right, null); left.parent = parent; right.parent = parent; // 将parent节点数据拷贝到"最小堆"中 heap.insert(parent); } mRoot = parent; // 销毁最小堆 heap.destroy(); }
首先建立最小堆,而后进入for循环。
每次循环时:
(01) 首先,将最小堆中的最小节点拷贝一份并赋值给left,而后重塑最小堆(将最小节点和后面的节点交换位置,接着将"交换位置后的最小节点"以前的所有元素从新构形成最小堆);
(02) 接着,再将最小堆中的最小节点拷贝一份并将其赋值right,而后再次重塑最小堆;
(03) 而后,新建节点parent,并将它做为left和right的父节点;
(04) 接着,将parent的数据复制给最小堆中的指定节点。
在二叉堆中已经介绍过堆,这里就再也不对堆的代码进行说明了。如有疑问,直接参考后文的源码。其它的相关代码,也Please RTFSC(Read The Fucking Source Code)!
哈夫曼树的源码共包括4个文件。