【数据结构学习】 赫夫曼树

引入

在日常生活中,对于一些比较大的文件,我们进行邮件或者微信发送的时候总是喜欢压缩一下,这样除了可以节省下来很多空间,还有很重要的一点,就是我们可以在网络上以压缩的形式传输大量数据,使得存储和保存十分的高效。

那么压缩时如何做到的呢?简单的说,就是把要压缩的文本进行重新编码,以减少不必要的空间。尽管现在最新技术在编码上已经很好很强大,但是这里介绍的是最基本的数据压缩编码方式---赫夫曼树。

在小学的时候,考试一般都是按照百分制来表示学科成绩的。这带来了一个弊端,就是让学生家长可能会那分数作比较,然后后来改革就变成了优秀、良好、中等、及格、不及格五个等级,不再通报具体分数。

例如一个学校将考试成绩转化为5个等级的算法如下图所示:

在实际情况中,大多数人的成绩应该是出于中等和良好,如果使用上面的比较程序,70分以上的人需要经过3次以上的判断才可以得到结论,这样是不合理的,如果将该结构改为下图,效率就会变得更高:

赫夫曼树的定义 

我们把上面的两棵树进行简化,E代表不及格、D代表及格、C代表中等、B代表良好、A代表优秀,那么上面的两个二叉树就可以简化为:

         图a                                                                                           图b

赫夫曼说,从树中一个结点到另一个结点之间的分支构成两个结点之间的路径,路径上的分支数目称为路径长度。二叉树a中,根节点到结点B的路径长度就是4,二叉树b的根节点到结点B的路径长度为2。树的路径长度是从树根到每一个结点的路径长度之和。二叉树a的路径长度就是:1+1+2+2+3+3+4+4=20,二叉树b的路径长度为:1+1+2+2+2+2+3+3=16。

如果考虑到带权的结点,树的带权路径长度叶子结点到树根之间的路径长度与结点上的权的乘积。给定n个权值作为n个叶子结点,构造一棵二叉树,如果该树的带权路径长度(WPL)达到最小,称这样的二叉树为最优二叉树,也称为赫夫曼树

赫夫曼树的创建步骤

  • 把所有权值的叶子结点按照从小到大的顺序进行排列,称为一个有序序列
  • 取出序列当中权值最小的两棵组成一个新二叉树,其中相对较小的为左孩子
  • 新二叉树的根节点的权值是两棵叶子结点的权值之和
  • 再将这棵新的二叉树,以根节点的权值大小在进行排序,不断重读上述步骤,直到所有数据都被处理,就得到了一个赫夫曼树

例如一个数列{13,7,8,3,29,6,1},要求转成一个赫夫曼树。

  • 排序后:1,3,6,7,8,13,29
  • 取出序列1和3组成一个新二叉树,其中相1为左孩子
  • 新二叉树的根节点的权值是1+3=4
  • 将这棵新的二叉树,以根节点的权值大小在进行排序:4,6,7,8,13,29
  • 取出序列4和6组成一个新二叉树,其中4为左孩子
  • 新二叉树的根节点的权值是4+6=10
  • 将这棵新的二叉树,以根节点的权值大小在进行排序:7,8,10,13,29
  • 取出序列7和8组成一个新二叉树,其中7为左孩子
  •  
  •  新二叉树的根节点的权值是7+8=15
  • 将这棵新的二叉树,以根节点的权值大小在进行排序:10,13,15,29
  • 取出序列10和13组成一个新二叉树,其中10为左孩子
  •  
  •   新二叉树的根节点的权值是10+3=23
  • 将这棵新的二叉树,以根节点的权值大小在进行排序:15,23,29
  • 取出序列15和23组成一个新二叉树,其中15为左孩子
  •  
  • 新二叉树的根节点的权值是23+15=38
  • 将这棵新的二叉树,以根节点的权值大小在进行排序:29,38
  • 取出序列29和38组成一个新二叉树,其中29为左孩子

上面这棵树就是一棵赫夫曼树。

代码实现

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class Huffman {
	
	public static void main(String[] args) {
		int[] arr = {1,3,6,7,8,13,29};
		Node1 root = createHaffman(arr);
		
		//测试
		preOrder(root);
	}
	
	//前序遍历二叉树
	public static void preOrder(Node1 root) {
		if(root != null) {
			root.preOrder();
		}else {
			System.out.println("空树!");
		}
	}
	
	
	//创建赫夫曼树
	public static Node1 createHaffman(int[] arr) {
		//数组不支持排序,取出放入arraylist中进行排序
		List<Node1> nodes = new ArrayList<Node1>();
		for(int value : arr) {
			nodes.add(new Node1(value));
		}
		
		while(nodes.size()>1) {
			//排序,从小到大
			Collections.sort(nodes);
			//取出权值最小的两个
			Node1 left = nodes.get(0);
			Node1 right = nodes.get(1);
			
			//构建新的二叉树
			Node1 parent = new Node1(left.value + right.value);
			parent.leftchild = left;
			parent.rightchild = right;
			
			//从arraylist中删除处理过的二叉树
			nodes.remove(left);
			nodes.remove(right);
			
			//将新的父结点加入到nodes
			nodes.add(parent);
			
		}
		return nodes.get(0);
	} 
	
}

//创建结点类
//为了让Node对象支持排序,需要实现Comparable接口
class Node1 implements Comparable<Node1>{
	int value; 
	Node1 leftchild;
	Node1 rightchild;
	
	public Node1(int value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	@Override
	public int compareTo(Node1 o) {
		// 从小到大进行排序
		return this.value-o.value;
	}
		
	
	public void preOrder() {
		System.out.println(this);
		if(this.leftchild != null) {
			this.leftchild.preOrder();
		}
		if(this.rightchild != null) {
			this.rightchild.preOrder();
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程芝士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值