哈夫曼树

首先,先复习一下二叉树的一些基本知识

树:是一种非线性数据结构。

二叉树:是每个结点最多有两个子树的有序树

根节点:最上面的节点

叶节点:没有子树的节点

深度:二叉树的层数,就是高度。

右图就是一个二叉树,A为根节点,D、E、F为叶节点,深度为2

那么什么是哈弗曼树呢?

哈夫曼树是最优二叉树,何为最优二叉树?就是每个叶节点到根节点的路径都是唯一且最短的。节点的权值越大则离根节点越远。其中,权值一般是该节点内容出现的频率。

大家可能会有疑问,为什么哈弗曼树是最优的呢?为什么可以起到压缩文件的作用呢?

我们学过的线性数据结构有数组、链表和栈,当数据以这种数据结构存储的时候,我们搜索数据就要依次遍历直至找到该数据。而哈夫曼树就简化了遍历的过程,我们在建哈夫曼树的规则是:

1.将数组数据从小到大排序。

2.生成一个新的节点,该节点的权值是最小的节点的权值和,该节点是最小两个节点的父节点。

3.在数组数据中除去最小的两个节点,加上新的节点。

4.排序

5.依次循环,只剩一个节点,该节点则为根节点。

因此,当我们在查找某个数据的时候,只要从根节点开始遍历,大于根节点在左子树,小于根节点在右子树。很快就可以找到。

而且,在存储数据的时候利用哈夫曼树也可以对数据进行压缩。例如:A B C D E 这几个字母,他们的权值分别是5,4,3,2,1.我们在进行存储的时候就可以如图利用他们的哈弗曼编码对数据进行压缩。

下面是我写的哈弗曼编码的java代码,其中实现了建树,和遍历输出哈弗曼编码

package Sameple0505哈弗曼树;
		/**
		 * 实现哈弗曼树的主要类
		 * @author Administrator
		 *
		 */
	public class Test{
		private Node[] leaf =null;//该树的叶节点数组
		private int length=0;//叶节点数组的长度
		private int size=0;
		private int count=0;//用来记录用户输入元素的个数
		private Node result =null;//初始化Node
		public Node getRoot(){
			return result;
		}
		public Test(int size){
			this.size=size;
			leaf=new Node[this.size];
		}
		public Test(){}
		/**
		 * 给叶节点数组添加值
		 * @param data 添加进来的叶节点内容
		 * @param weight 添加进来的叶节点的权值
		 */
	    public void addNode(Object data,int weight){
	    	Node temp =new Node(data,weight);
	    	leaf[length++]=temp;
		}
	   /**
	    * 给节点进行冒泡排序,从小到大排序,返回排好序的数组
	    * @param nodes
	    * @return
	    */
	    public Node[] SortNode(Node[] nodes){
	    	for(int i=0;i<nodes.length;i++){
	    		for(int j=i+1;j<nodes.length;j++){
	    		if(nodes[i].getwgt()>nodes[j].getwgt()){
	    			Node tem=nodes[i];
	    			nodes[i]=nodes[j];
	    			nodes[j]=tem;
	    		}
	    	 }
	      }
	    	return nodes;
	    }
		    /**
		     * 建立哈弗曼树,从下往上建树
		     * @return
		     */
				public Node createTree(){
					//判断哈弗曼树是否建好,如果建好则只剩根节点,length为1
					while(length>1){
						leaf=SortNode(leaf);//先按权值排序
						//新节点的权值为左右节点之和
						result=new Node(leaf[0].getwgt()+leaf[1].getwgt());
						//分别给左右节点设置父节点
						leaf[0].setFather(result);
						leaf[1].setFather(result);
						result.setRight(leaf[0]);//第二个元素为左节点,左边大,右边小
						leaf[0].setPosition(0);//添加位置参数
						result.setLeft(leaf[1]);//设置第一个元素为右节点
						leaf[1].setPosition(1);//添加位置参数
						leaf=newNode(leaf, 2);
						length--;
						leaf[length-1]=result;
					}
					System.out.println(result.getwgt());
					return result;
			     	}
				/**
				 * 建树过程中不断减少的的数组
				 * @param nodes 新的数组
				 * @param num  减少的个数
				 * @return
				 */
				public Node[] newNode(Node[] nodes,int num){
				    Node[] newnodes=new Node[nodes.length-num+1];
					for (int i = 0; i < nodes.length-num; i++) {
						newnodes[i]=nodes[i+num];
					}
					return newnodes;
				}
				/**
				 * 从根节点开始遍历哈弗曼树,打印出叶节点上字母的哈弗曼编码
				 * @param args
				 */
				public void printhfm(Node leaf){
					if (leaf.getLeft()==null&&leaf.getRight()==null) {
						//输出叶节点的内容
						System.out.println(leaf.getData()+"的哈弗曼编码是: ");
						String posString="";//储存哈弗曼编码
						while(leaf.getFather()!=null){
							posString=leaf.getPosition()+posString;
							leaf=leaf.getFather();
						}
						System.out.println(posString);
					}else {	
						printhfm(leaf.getLeft());
					    printhfm(leaf.getRight());
					}
					}
				
				/**
				 *包装方法
				 */
				public void showcode(){
					Node temp=createTree();
					printhfm(temp);
				}
				

	    }
	     

 

这是哈夫曼树的节点类:

package Sameple0505哈弗曼树;

/**
 * 二叉树节点类
 * @author 邵珂
 *
 */

public class Node {
	private Object data;//节点的内容
	private int weight;//节点的权值
	private Node father=null;//父节点
    private Node left=null;//左孩子
	private Node right=null;//右孩子
	private String position=null;//设置该节点在父节点的位置,左边添加1,右边添加0
	/**
	 * 重写构造方法,创建叶节点时使用
	 * @param data  传入的叶节点的内容
	 * @param weight 叶节点的权值
	 */
	public Node(Object data,int weight) {
		this.data = data;
		this.weight=weight;
	}
	/**
	 * 重写构造方法,创建非叶结点时使用
	 * @param weight 权值
	 */
	public Node(int weight){
		this.weight=weight;
	}
	/**
	 * 默认的构造方法
	 */
	public Node(){}
	/**
	 * 返回该节点存储的内容
	 * @return
	 */
	public Object getData() {
		return data;
	}

	/**
	 * 返回该节点的权值
	 * @return
	 */
	public int getwgt(){
		return weight;
	}
	/**
	 * 返回该节点的父节点
	 * @return
	 */
	public Node getFather() {
		return father;
	}
	/**
	 * 设置该节点的父节点
	 * @param father 父节点
	 */
	public void setFather(Node father) {
		this.father = father;
	}
	/**
	 * 返回该节点的左孩子
	 * @return
	 */
	public Node getLeft() {
		return left;
	}
	/**
	 * 设置该节点的左孩子
	 * @param left
	 */
	public void setLeft(Node left) {
		this.left = left;
	}
	/**
	 * 返回该节点的右孩子
	 * @return
	 */
	public Node getRight() {
		return right;
	}
	/**
	 * 设置该节点的右孩子
	 * @param right
	 */
	public void setRight(Node right) {
		this.right = right;
	}
	/**
	 * 获取该节点是处于父节点的左边还是右边
	 * @return
	 */
	public String getPosition() {
		return position;
	}
	/**
	 * 设置该节点是在父节点的左边还是右边,左边传1,右边传0
	 * @param position
	 */
	public void setPosition(int position) {
		this.position = ""+position;
	}
	
	
	
	
	

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值