Java学习笔记:数据结构之哈夫曼树(带编码)

哈夫曼树概念

二叉树概念:
  • 一个节点下最多有两个子节点
  • 结点构成:数据域+地址域
  • 相关术语
    结点(Node):包含一个数据元素及若干指向子树的分支
    子节点(child Node):结点的子树
    结点层:根结点层定义为1,根的子节点为第二层,依次类推
    深度:树中最大的结点层(由上至下)
    高度:叶子结点的高度为1,往上节点的高度依次递增(由下往上)
    :结点子树的个数
    叶子结点:度为0的结点(终端结点)
    分支结点:度不为0的结点
  • 二叉树分类
    完全二叉树—二叉树的除最底层外,其他层的结点数都达到最大个数,且叶子结点都是从左到右依次排布
    在这里插入图片描述
    满二叉树—除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树
    在这里插入图片描述
    平衡二叉树—AVL树:是一颗空树或者左右两个子树的高度差绝对值不超过1,且左右两个子树都是一颗平衡二叉树
    在这里插入图片描述
  • 二叉树的遍历:根据根结点的位置判断
    先序遍历:根左右
    中序遍历:左根右
    后序遍历:左右根
哈夫曼树
  • 哈夫曼树又称最优二叉树,是一类带权路径最短的树。
  • 结点之间的路径长度:从一个结点到另一个结点之间的分支数目
  • 树的路径长度:从树的根到树中每一个结点的路径长度之和
  • 结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积
  • 树的带权路径长度:树中所有叶子结点的带权路径长度之和:
    在这里插入图片描述
    一般树:
    在这里插入图片描述
    哈夫曼树:
    在这里插入图片描述

哈夫曼数实现

  1. 根据给定的n个权值{w1,w2,w3,…wn}对这个序列排序
  2. 取出最小的两个权值使之为左右子结点建树,得出父亲结点,父亲节点的权值等于左右子节点权值相加
  3. 由2得出的父亲结点放入序列中
  4. 重复1,2,3操作直到序列只有1个结点,此时得出的就是根节点

哈夫曼数编码

原则:左0右1
在这里插入图片描述

哈夫曼树实例

结点:

package com.HFMTree;

public class Node {
	public char data;
	public int count;
	public Node left;
	public Node right;
	public Node(char d,int c){
		data=d;
		count=c;
	}
}

接口:

package com.HFMTree;


import com.MyArrayList.MyArrayList;


public interface HFMTreeInterface {

	void printHFMTree(Node node, String hfm_code);

	/**
	 * 創建哈夫曼二叉樹的方法
	 * 
	 * @param list存储节点的数组队列对象
	 * @return 返回哈夫曼二叉树的根节点
	 */
	Node createHFMTree(MyArrayList<Node> list);

	/**
	 * 根据频率进行排序的方法
	 * 
	 * @param list要排序的数组队列
	 */
	void sort(MyArrayList<Node> list);

	/**
	 * 根据字符和频率创建节点的方法
	 * 
	 * @param array数组中存储的是字符和频率
	 * @return 返回数组队列对象
	 */
	MyArrayList<Node> createNode(int[] array);

	void printArray(int[] array);

	/**
	 * 读取文件的,并统计每一个字符出现的频率
	 * 
	 * @param path读取的文件路径
	 * @return 返回每一个字符出现的频率
	 */
	int[] readFile(String path);

	
}

实现:

package com.HFMTree;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.HFMTree.HFMTreeInterface;
import com.MyArrayList.MyArrayList;

public class HFMTree implements HFMTreeInterface{
	/**
	 * 主函数
	 * 先读取文件abc.txt(全路径)
	 * 打印保存好的数组,检查是否正确
	 * 
	 * @param arge
	 */
	public static void main(String [] arge){
		HFMTree hfm=new HFMTree();
		int []array=new int [256];
		array=hfm.readFile("D:/learning/mydemo/Javaworkspace/rect/src/com/HFMTree/abc.txt");
		hfm.printArray(array);
		MyArrayList<Node> list=hfm.createNode(array);
		hfm.sort(list);
		Node root=hfm.createHFMTree(list);
		hfm.printHFMTree(root, "");
	}
	/**
	 * 遍历打印哈夫曼树
	 */
	public void printHFMTree(Node node, String hfm_code){
		if(node.left != null)
			printHFMTree(node.left, hfm_code+'0');
		if(node.right!=null)
			printHFMTree(node.right,hfm_code+'1');
		if(node.left==null&&node.right==null)
			System.out.println(node.data+" >"+hfm_code);
	}

	/**
	 * 创建哈夫曼树的方法
	 * 
	 * @param list存储节点的数组队列对象
	 * @return 返回哈夫曼二叉树的根节点
	 */
	public Node createHFMTree(MyArrayList<Node> list){
		while(list.getsize()>1){
			sort(list);
			Node left=list.remove(0);
			Node right=list.remove(0);
			Node father=new Node((char)(left.data+right.data),left.count+right.count);
			list.add(father);
			father.left=left;
			father.right=right;
		}
		return list.getObject(0);
	}

	/**
	 * 根据频率进行排序的方法
	 * 降序排序
	 * @param list要排序的数组队列
	 */
	public void sort(MyArrayList<Node> list){
		if(list.getsize()>0){
			for(int i=0;i<list.getsize()-1;i++){
				
				int min=i;
				for(int j=i+1;j<list.getsize();j++){
					if(list.getObject(i).count>list.getObject(j).count)
						min=j;
				}
				Node n=list.remove(min);
				list.add(i, n);
			}
		}
	}

	/**
	 * 根据字符和频率创建节点的方法
	 * 
	 * @param array数组中存储的是字符和频率
	 * @return 返回数组队列对象
	 */
	public MyArrayList<Node> createNode(int[] array){
		if(array.length>0){
			MyArrayList<Node> list=new MyArrayList<Node>();
			for(int i=0;i<array.length;i++){
				if(array[i]>0){
					Node n=new Node((char)i,array[i]);
					list.add(n);
				}
			}
			return list;
		}
		return null;
	}
	/**
	 * 输出保存的字符数组:字符+个数
	 * (non-Javadoc)
	 * @see com.HFMTree.HFMTreeInterface#printArray(int[])
	 */
	public void printArray(int[] array){
		for(int i=0;i<array.length;i++){
			if(array[i]>0){
				System.out.println((char)i+" > "+array[i]);
			}
		}
	}

	/**
	 * 读取文件并统计每一个字符出现的频率
	 * 保存到array数组中
	 * @param path读取的文件路径
	 * @return 返回每一个字符出现的频率
	 */
	public int[] readFile(String path) {
		int[] array = new int[256];// 创建一个数组对象,从来存储每一个字符的频率
		try {
			FileInputStream in = new FileInputStream(path);// 实例化字节输入流对象
			int i = 0;
			// 循环读取文件中的数据 i存储的是字符对应的ascii
			while ((i = in.read()) != -1) {// 判断是否还未到文件末尾
				array[i]++;// 将字符出现的频率增加1
			}
			in.close();// 关闭输入流对象
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return array;
	}
}

读取的文件:

After our daughters were born in 1960 and 1970, I 
started buying special ornaments for them every year and putting them
 on our tree. I’d mark the year and the recipient on each ornament, forming 
 a starter set for each daughter. Later, when they married and moved out, each
 received her own traditional ornament collection.
Just about that time, my mother started to sort through her prized 
collectibles to give to my siblings and me. I was blessed to receive more of 
them from one sibling who is not a saver.
Now each year, I choose a new selection of these fragile and beautiful
ancestral ornaments to hang near the tiny white lights on my little tree.
They give me such joy as I give thanks for my mother, the real collector
of memories.

运行结果:


 > 11

 > 11
  > 129
, > 7
. > 7
0 > 2
1 > 2
6 > 1
7 > 1
9 > 2
A > 1
I > 5
J > 1
L > 1
N > 1
T > 1
a > 44
b > 8
c > 18
d > 17
e > 83
f > 11
g > 14
h > 31
i > 32
j > 1
k > 2
l > 20
m > 24
n > 38
o > 47
p > 4
r > 49
s > 28
t > 59
u > 13
v > 8
w > 8
y > 13
z > 1
? > 1
? > 1
i >0000
b >000100
1 >00010100
9 >00010101
k >00010110
j >000101110
T >000101111
d >00011
n >0010
c >00110
l >00111
e >010
N >011000000
L >011000001
J >011000010
A >011000011
I >0110001

 >011001

 >011010
f >011011
a >0111
o >1000
m >10010
z >100110000
? >1001100010
? >1001100011
0 >10011001
7 >100110100
6 >100110101
p >10011011
y >100111
r >1010
u >101100
g >101101
s >10111
t >1100
, >1101000
. >1101001
w >1101010
v >1101011
h >11011
  >111

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值