哈夫曼树

本文详细介绍了哈夫曼树的构造原理及其在数据压缩中的应用,通过哈夫曼编码,可以对不同频率的字符采用不等长的二进制位表示,从而有效减少数据存储空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

哈夫曼树:

  当树中的节点被赋予一个表示某种意义的数值,我们称之为该节点的权。从树的根节点到任意节点的路径长度(经过的边数)与该节点上权值的乘积称为该节点的带权路径长度。树中所有叶节点的带权路径长度之和称为该树的带权路径长度(WPL)。当带权路径长度最小的二叉树被称为哈夫曼树,也成为最优二叉树。

哈夫曼树的构造:

  哈夫曼树在构造时每次从备选节点中挑出两个权值最小的节点进行构造,每次构造完成后会生成新的节点,
将构造的节点从备选节点中删除并将新产生的节点加入到备选节点中。新产生的节点权值为参与构造的两个节点权值之和。

构造哈夫曼树的算法:

1)对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F={T1,T2,T3,...,Ti,...,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。

2)在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。

3)从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。 4)重复2)和3),直到集合F中只有一棵二叉树为止。

下面来看代码:

public class HaffManCode {

	private static class Node implements Comparable<Node> {
		// 权值
		int val;
		// 左节点
		Node left;
		// 右节点
		Node right;

		public Node(int val) {
			this.val = val;
		}

		@Override
		public int compareTo(Node o) {
			return this.val - o.val;
		}
	}

	/**
	 * @param W 权值数组
	 * @return
	 */
	public Node createHaffManTree(int[] W) {
		// 利用堆实现动态自动排序
		PriorityQueue<Node> f = new PriorityQueue<Node>();
		for (int e : W) {
			f.offer(new Node(e));
		}
		while (f.size() > 1) {
			// 每次取权值最小的两个节点构建一个新的父节点且新节点参与到新的一轮构建中
			Node min1 = f.poll();
			Node min2 = f.poll();
			Node parent = new Node(min1.val + min2.val);
			parent.left = min1;
			parent.right = min2;
			f.offer(parent);
		}
		return f.poll();
	}
    //控制台打印树
	public void showTree(Node t) {
		int level = countLevel(t);
		countLevel(t);
		LinkedList<Node> que = new LinkedList<>();
		que.add(t);
		int curLevel = 1;
		while (!que.isEmpty() && curLevel <= level) {
			int len = que.size();
			// 每行左边所需空格数
			int space_num = (int) (Math.pow(2, level - curLevel)) - 1;
			// 左边的空格
			while (space_num >= 1) {
				System.out.print(" ");
				space_num--;
			}
			for (int i = 0; i < len; i++) {
				Node node = que.poll();
				// 每行节点间的空格数
				int between_e_space = (int) Math.pow(2, level + 1 - curLevel) - 1;
				// 空节点
				if (node.val == Integer.MIN_VALUE) {
					System.out.print(" ");
				} else {
					System.out.print(node.val);
				}
				while (between_e_space >= 1) {
					System.out.print(" ");
					between_e_space--;
				}
				if (node != null) {
					if (node.left == null) {
						// 空占位节点
						que.add(new Node(Integer.MIN_VALUE));
					} else {
						que.add(node.left);
					}
					if (node.right == null) {
						que.add(new Node(Integer.MIN_VALUE));
					} else {
						que.add(node.right);
					}
				}
			}
			// 换行
			System.out.println();
			curLevel++;
		}

	}
    //计算树的高度
	private static int countLevel(Node t) {
		if (t == null)
			return 0;
		LinkedList<Node> que = new LinkedList<>();
		que.add(t);
		int level = 0;
		while (!que.isEmpty()) {
			level++;
			int len = que.size();
			for (int i = 0; i < len; i++) {
				Node node = que.poll();
				if (node != null) {
					if (node.left != null)
						que.offer(node.left);
					if (node.right != null)
						que.offer(node.right);
				}
			}
		}
		return level;
	}

	public static void main(String[] args) {
		HaffManCode haffMan = new HaffManCode();
		int[] w = { 2,5,1,6,7};
        haffMan.showTree(haffMan.createHaffManTree(w));
	}
}

 应用: 在处理字符串序列时,如果对每个字符串采用相同的二进制位来表示,则称这种编码方式为定长编码。若允许对不同的字符采用不等长的二进制位进行表示,那么这种方式称为可变长编码。 可变长编码其特点是对使用频率高的字符采用短编码,而对使用频率低的字符则采用长编码的方式。这样我们就可以减少数据的存储空间,从而起到压缩数据的效果。而通过哈夫曼树形成的哈夫曼编码是一种的有效的数据压缩编码。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值