Java基础 - 哈夫曼树及其实现


package com.yc.tree;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

/**
 * 
 * @author wb
 * 哈夫曼树又被称为最有二叉树,是一种带权路径最短的二叉树。哈夫曼树是二叉树的一种简单应用,在信息检索中很常用。
 * 哈夫曼树的定义和基本概念:
 * 在介绍哈夫曼树之前先来介绍一些相关的概念.
 * 	(1)、节点之间的路径长度:从一个节点到另一个节点之间的分支数量称为两个节点之间的路径长度。
 * 	(2)、树的路径长度:从根节点到树中每个节点的路径长度之和。
 * 	(3)、节点的带权路径长度:从该节点到根节点之间的路径长度与节点的权的乘积。
 * 	(4)、树的带权路径长度:树中所有叶子节点的带权路径长度之和。
 * 带权路径最小的二叉树称为哈夫曼树或最优二叉树。
 * 对于哈夫曼树有一个非常重要的定理:对于具有 n 个叶子节点的哈夫曼树,一共需要 2*n - 1 个节点。因为对于二叉树来说,有三种类型的节点,
 * 即度为2的节点,度为1的节点和度为0的叶子节点,而哈夫曼树的非叶子节点都是由两个节点合并而成的,所以不会出现度为1的节点,而生成的非叶子节点
 * 的数量等于叶子节点的数量 - 1,因此 n 个叶子节点的哈夫曼树一共需要 2*n - 1个节点。
 * 				  0 A							 ⊙
 * 					│						    ││
 * 			   1 B──┘						 ⑦──┘└──⊙ 
 * 				││							1*7		││
 * 		   2 C──┘└─D 2							 ⑤──┘└──⊙ 
 * 					│							2*5		││
 * 					└──E 3							 ②──┘└──④ 
 * 					   │							3*2		3*4
 * 				  4 F──┘				
 * 					│
 * 					└──G 5	
 * 			图11.16 二叉树的路径长度				图11.17 树的带权路径长度
 * 创建哈夫曼树
 * 创建哈夫曼树的步骤如下:
 * 	①根据给定的n个权值{w1,w2,……,wn}构造 n棵二叉树的集合F={T1,T2,……,Tn},F集合中每棵二叉树都只有一个根节点。
 * 	②选取F集合中两棵根节点的权值最小的树作为左、右子树以构造一棵新的二叉树,且将新的二叉树的根节点的权值设为左、右子树上根节点的权值之和。
 * 	③将新的二叉树加入到F集合中,并删除第2步中被选中的两棵树。
 * 	④重复2、3步,直到F集合中只剩下一棵树,这棵树就是哈夫曼树。
 * 如下图显示了哈夫曼树的创建过程:
 * 	
 * 7 5 2 4 ->  7 5 6	-> 7 11		->	18		->	¤
 * 				   ││		 ││			││			││
 * 				  2┘└4      5┘└─6	   7┘└─11	   7┘└─¤
 * 								││		   ││		   ││
 * 							   2┘└4   	  5┘└─6		  5┘└─¤	
 * 										      ││	      ││
 * 										     2┘└4		 2┘└4
 * 
 * 下面程序中创建哈夫曼树的关键代码就是粗体部分,当然,每次在产生的新的节点加入集合树的再次快速排序也同样重要。
 * 这些粗体代码完成了如下事情:
 * (1)对list集合里的所有节点进行排序;
 * (2)找出list集合中权值最小的两个节点;
 * (3)以权值最小的节点作为新节点的左子节点;
 * (4)从list集合中删除权值最小的两个节点,将新节点加入list集合。
 * 程序采用循环不断地执行上面的4步,知道list集合中只剩下一个节点,最后的剩下的这个节点就是形成的huffMan树的根节点。
 * 基于上面的思想,对指定节点集合创建哈夫曼树,实例如下。
 */
public class HuffManTree {
	 public static class Node<E>{
		 E data;
		 double weight;
		 Node<E> left;
		 Node<E> right;
		 public Node(E data, double weight, Node<E> left, Node<E> right){
			 this.data = data;
			 this.weight = weight;
			 this.left = left;
			 this.right = right;
		 }
		 public String toString(){
			 return "Node[data=" + data + ", weight=" + weight + "]";
		 }
	 }
	 
	 /**
	  * 创建哈夫曼树
	  * @param nodes:节点集合
	  * @return		:返回创建的哈夫曼树的根节点
	  */
	 public static <E> Node<E> createTree(List<Node<E>> nodes){
		 
		 //只要nodes集合中还有两个以上的节点
		 while(nodes != null && nodes.size() > 1){
			 quickSort(nodes);
			 
			 Node<E> left = nodes.get(nodes.size() - 1);
			 Node<E> right = nodes.get(nodes.size() - 2);
			 
			 Node<E> parent = new Node<E>(null, left.weight + right.weight, left, right);
			 
			 nodes.remove(nodes.size() - 1);
			 nodes.remove(nodes.size() - 1);
			 
			 nodes.add(parent);
		 }
		 return nodes.get(0);
	 }
	 
	 //快速排序
	private static <E> void quickSort(List<Node<E>> nodes){
		subSort(nodes, 0, nodes.size() - 1);
	}
	private static <E> void subSort(List<Node<E>> nodes, int start, int end) {
		if(start < end){
			//以第一个元素作为分界值
			Node<E> base = nodes.get(start);
			int i = start;
			int j = end + 1;
			while(true){
				while(i < end && nodes.get(++ i).weight >= base.weight);
				while(j > start && nodes.get(-- j).weight <= base.weight);
				if(i < j){
					swap(nodes, i, j);
				}else{
					break;
				}
			}
			swap(nodes, start, j);
			//
			subSort(nodes, start, j - 1);
			//
			subSort(nodes, j + 1, end);
		}
	}
	private static <E> void swap(List<Node<E>> nodes, int i, int j){
		Node<E> temp;
		temp = nodes.get(i);
		nodes.set(i, nodes.get(j));
		nodes.set(j, temp);
	}
	
	//广度优先遍历
	public static <E> List<Node<E>> breadthFirstSearch(Node<E> root){
		List<Node<E>> nodes = new ArrayList<Node<E>>();
		Deque<Node<E>> deque= new ArrayDeque<Node<E>>();
		
		if(root != null){
			deque.offer(root);
		}
		
		while(!deque.isEmpty()){
			Node<E> tmp = deque.poll();
			nodes.add(tmp);
			
			if(tmp.left != null){
				deque.offer(tmp.left);
			}
			if(tmp.right != null){
				deque.offer(tmp.right);
			}
		}
		return nodes;
	}
	
	public static void main(String[] args) {
		List<Node<String>> nodes = new ArrayList<Node<String>>();
		nodes.add(new Node<String>("A", 40.0, null, null));
		nodes.add(new Node<String>("B", 7.0, null, null));
		nodes.add(new Node<String>("C", 10.0, null, null));
		nodes.add(new Node<String>("D", 30.0, null, null));
		nodes.add(new Node<String>("E", 12.0, null, null));
		nodes.add(new Node<String>("F", 2.0, null, null));
		
		Node<String> root =  HuffManTree.createTree(nodes);
		System.out.println( breadthFirstSearch(root));
	}
	/**
	 * 根据上面的main方法可以得到一棵如下的哈夫曼树:
	 * 					(101.0)
	 * 					   ││
	 * 			(A,40.0)───┘└───(61.0)
	 * 							 ││
	 * 				  (D,30.0)───┘└───(31.0)
	 * 									││
	 * 						 (E,12.0)───┘└───(19.0)
	 * 											││
	 * 									(9.0)───┘└───(C,10.0)
	 * 									 ││
	 * 						   (F,2.0)───┘└───(B,7.0)
	 */

}

测试结果如下:

[Node[data=null, weight=101.0], Node[data=A, weight=40.0], Node[data=null, weight=61.0], Node[data=D, weight=30.0], Node[data=null, weight=31.0], Node[data=E, weight=12.0], Node[data=null, weight=19.0], Node[data=null, weight=9.0], Node[data=C, weight=10.0], Node[data=F, weight=2.0], Node[data=B, weight=7.0]]


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值