基本介绍
(一)基本介绍
1)给定n个权值作为n个叶子节点,构造一颗二叉树,如果这个树的带权路径长度达到最小,就叫做“最优二叉树”,也叫“赫夫曼树”
带权路径最短=最优二叉树=赫夫曼树
2)赫夫曼树是带权路径长度最短的树,权值较大的节点离根很近
(二)重要概念
(三)赫夫曼树创建思路
一个数列{13,7,8,3,29,6,1},要求转成一颗赫夫曼树
1)从小到大进行排序,每一个数据都是一个节点,每个节点可以看成是一颗最简单的二叉树——{1,3,6,7,8,13,29}
2)取出根节点权值最小的两颗二叉树——取出{1,3}
3)组成一颗新的二叉树,这个新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和——1+3=4,新树根节点为4,子节点为1,3
4)再把这个新的二叉树,根据根节点的权值大小再次排序,不断重复1-2-3-4的步骤
直到数列中,所有的数据都被处理,就得到一颗赫夫曼树——然后{4,6,7,8,13,29},选出{4,6},根节点为10,继续{7,8,10,13,29}
(四)赫夫曼编码
1)是一种编码方式,属于一种程序算法
2)赫夫曼编码是赫夫曼树的一种应用,主要应用在电讯通信中
3)赫夫曼编码广泛的用于数据文件压缩,压缩率通常在20%~90%之间
4)赫夫曼码是“可变字长编码”的一种,称为最佳编码
(五)赫夫曼编码原理剖析
在通信领域里信息的处理方式1-定长编码
1)一个字符串,共40个字符串(包括空格)
2)先转换成对应的Ascii码
3)再把ASCII码转换成对应的二进制
4)按照二进制来传递信息,总的长度是359(包括空格)
编码过程:
1)首先给个字符串
2)统计各个字符出现的次数
3)根据字符出现的次数构件一棵赫夫曼树,次数作为权值
4)根据赫夫曼树,给各个字符规定编码(前缀编码),向左的路径为0,向右的路径为1
这样赫夫曼树里的每一个字符都有自己独有的一个编码
5)按照上面的赫夫曼编码,字符串的字符就可以替换成编码
注意:
这个赫夫曼树根据排序方法不同,也可能不太一样,这样对应的赫夫曼编码也不完全一样,但是wpl是一样,都是最小的,
比如:如果我们让每次生成的新的二叉树总是排的权值相同的二叉树的最后一个,则生成的二叉树
通信领域中信息的处理方式2-变长编码
1)一个字符串,共40字符(包括空格)
2)各个字符对应的出现的个数
3)按照各个字符出现的次数进行编码,原则是出现次数越多的,则编码越小,比如空格出现出现了9次,编码为0,其他依次类推
4)按照上面给各个字符规定的编码,把字符串改写成上面的编码
5)字符的编码都不能是其他字符编码的前缀,符合此要求的编码叫做前缀编码,即不能匹配到重复的编码(这个赫夫曼编码中)
通信领域中信息的处理方式3-赫夫曼编码
思路:
1)Node节点类,data放数据,weight放权值,left和right指针
2)得到字符串对应的byte[]数组
3)编写一个方法,把准备构件赫夫曼树的Node节点放到List
4)通过List创建对应的赫夫曼树
创建节点类
//首先创建节点
//为了让Node对象持续排序Collections集合排序,让Node实现Comparable
class Node implements Comparable<Node> {
int value;//节点的权值
Node left;//指向左子节点
Node right;//指向右子节点
//写一个前序遍历
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if(this.right != null){
this.right.preOrder();
}
}
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
@Override
public int compareTo(Node node) {
//表示从小到大排序
return this.value - node.value;
}
}
创建赫夫曼树
public class HuffmanTree {
public static void main(String[] args) {
int arr[] = {13, 7, 8, 3, 29, 6, 1};
//测试生成一棵赫夫曼树,获取树的根节点
Node root=createHuffmanTree(arr);
//测试前序遍历赫夫曼树
preOrder(root);//
}
//编写一个前序遍历的方法
public static void preOrder(Node root){
if(root!=null){
root.preOrder();
}else {
System.out.println("树为空,不能遍历");
}
}
//创建赫夫曼的方法
public static Node createHuffmanTree(int[] arr) {
//第一步为了操作方便
//1-遍历arr数组
//2-把arr的每个元素构成成一个Node
//3-把Node放入到ArrayList中
List<Node> nodes = new ArrayList<Node>();
for (int value : arr) {
//每遍历出来一个value,就放进node对象
nodes.add(new Node(value));
}
//循环创建
while(nodes.size()>1){
//首先把数组排序,从小到大,直接使用一个集合
Collections.sort(nodes);
System.out.println("nodes=" + nodes);
//取出根节点权值最小的两棵二叉树
//(1)取出权值最小的节点(二叉树)
Node leftNode = nodes.get(0);
//(2)取出权值第二小的节点(二叉树)
Node rightNode = nodes.get(1);
//(3)创建一棵新的二叉树
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
//(4)从ArrayList删除处理过的二叉树
nodes.remove(leftNode);
nodes.remove(rightNode);
//(5)把parent加入nodes
nodes.add(parent);
Collections.sort(nodes);
System.out.println("第一次处理后:" + nodes);
}
return nodes.get(0);
}
}