Java----哈夫曼树的构建

什么是哈夫曼树 ?

给定n个权值作为n个叶子节点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树。哈夫曼树是带权路径长度最短的树,权值较大的节点距离根节点较近。

基本术语

  • 路径和路径长度

在一棵树中,从一个节点往下可以达到的孩子或孙子节点之间的通路,称为路径。通路中分支的数目称为路径长度

  • 节点的权值及带权路径的长度

若将树中节点赋一个有着某种含义的数值,则这个数值称为该节点的( 哈夫曼树中的权通常指节点出现的次数/频率 ),节点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积。

  • 树的带权路径长度

树的带权路径长度规定为:所有叶子节点的带权路径长度之和,记为WPL。

情景:
考虑这样一个问题,现在我们要对学生的考试成绩进行统计,考试成绩的等级有 A、B、C、D 四个等级,处于四个等级中的学生人数分别为10、50、60、5人。要编写这样一个代码,其实是非常easy的,只需要用if、else即可。

代码一:
if(A){
		
	}else if(B){
		
	}else if(C){
		
	}else {
		
	}
执行次数:10 + 50*2 + 60*3 + 5*3 = 305(次)
代码二:
if(B){
		
	}else if(C){
		
	}else if(D){
		
	}else {
		
	}
	执行次数:50 + 60*2 + 5*3 + 10*3 = 215

可以发现代码一和代码二的结构是一样的,但是两份代码的执行次数却差了将近100次,如果我们想寻找一种最优化的方案,那么我们又该如何排序呢?或许二叉树可以办到呢!

哈夫曼树的实现步骤:

  1. 统计字符串中的字符以及出现的次数,将节点存放在数组队列中
  2. 按照节点的权值排序
  3. 取出并删除权值最小的两节点,这两个节点的父节点的数据为两个节点的数据相加,权值为两节点的权值相加,将两个节点构建为新节点。将节点放回数组队列中并排序(保证节点的权值有序)
  4. 重复步骤3

Example:

根据字符串 aaabbccccddddddee 构建哈夫曼树

节点  权值
  e       2
  b       2
  a       3
  c       4
  d       6

这里写图片描述

节点  权值
  a       3
  eb      4
  c       4
  d       6

这里写图片描述

重复上述步骤知道数组队列中只有一个节点。

注意:
哈夫曼树的结构不唯一,但是树的带权路径长度是唯一的。

代码实现

Node类


public class Node {
	
	Node left,right;
	String data;
	int weight;
	
	public Node(){}
	
	public Node(String data){
		this.data = data;
	}
	
	public void setLeft(Node left){
		this.left = left;
	}
	
	public Node getLeft(){
		return left;
	}
	
	public void setRight(Node right){
		this.right = right;
	}
	
	public Node getRight(){
		return right;
	}
	
	public void setWeight(int weight){
		this.weight = weight;
	}
	
	public int getWeight(){
		return weight;
	}
	
	public void setData(String data){
		this.data = data;
	}
	
	public String getData(){
		return data;
	}

}

Tree类


public class HalfTree {
	
	static Node root;
	String s = "";
	//用来存放节点和权值
	ArrayList<Node> list = new ArrayList<>();

	
	public void create_tree(String str){
		//统计字符及出现的次数
		int[] count = new int[128];
		for(int i=0;i<str.length();i++){
			char ch =  str.charAt(i);
			count[ch]++;
		}
		for(int i=0;i<count.length;i++){
			if(count[i]!=0){
				char ch = (char) i;		//强制转换为char类型
				int number = count[i];
				Node node = new Node(ch+"");
				node.weight = number;
				list.add(node);
			}
		}
		
		while(list.size()>1){
			//根据权值进行排序
			this.sort();
			Node left = list.remove(0);
			Node right = list.remove(0);
			String s = left.data + right.data;
			Node father = new Node(s);
			father.setWeight(left.weight+right.weight);
			father.setLeft(left);
			father.setRight(right);
			list.add(0,father);
		}
		root = list.get(0);
	}
	
	//根据节点的权值从小到大进行排序
	public void sort(){
		Node temp;
		for(int i=0;i<list.size();i++){
			for(int j=i+1;j<list.size();j++){
				if(list.get(i).weight > list.get(j).weight){
					temp = list.get(i);
					list.set(i, list.get(j));
					list.set(j, temp);
				}
			}
		}
	}
	
	//先序遍历(根--->左--->右)
	public void before_output(Node node){
		if(node==null){
			return ;
		}else {
			System.out.println(node.getData()+" "+node.getWeight());
			before_output(node.getLeft());
			before_output(node.getRight());
		}
	}
	
	//中序遍历(左---根--->右 )
	public void middle_output(Node node){
		if(node.getLeft()!=null){
			middle_output(node.getLeft());
		}
		System.out.println(node.getData()+" "+node.getWeight());
		if(node.getRight()!=null){
			middle_output(node.getRight());
		}
	}
	
	//后序遍历(左--->右--->根)
	public void behind_output(Node node){
		if(node.getLeft()!=null){
			behind_output(node.getLeft());
		}
		if(node.getRight()!=null){
			behind_output(node.getRight());
		}
		System.out.println(node.getData()+" "+node.getWeight());
	}
	
	
	public static void main(String[] args){
		HalfTree tree = new HalfTree();
		tree.create_tree("aaabbccccddddddee");
		System.out.println("先序遍历:");
		tree.before_output(root);
		System.out.println("----------------");
		System.out.println("中序遍历:");
		tree.middle_output(root);
		System.out.println("----------------");
		System.out.println("后序遍历:");
		tree.behind_output(root);
	}
	

}

样例输出:

先序遍历:
abecd 17
abe 7
a 3
be 4
b 2
e 2
cd 10
c 4
d 6
----------------
中序遍历:
a 3
abe 7
b 2
be 4
e 2
abecd 17
c 4
cd 10
d 6
----------------
后序遍历:
a 3
b 2
e 2
be 4
abe 7
c 4
d 6
cd 10
abecd 17

可承接各种项目,有意者加QQ:1217898975

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

steven_moyu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值