哈夫曼树和哈夫曼编码(java)

哈夫曼树和哈夫曼编码(java)

哈夫曼树的概念,参考博客哈夫曼树

哈夫曼树,也就是带权路径长度最小的二叉树,最优二叉树。
构造一个最优二叉树,每次都取最小的两个元素构成树。

具体代码实现:
1、哈夫曼结点类

/**
 * 哈夫曼节点类
 * 
 * @author kiki
 *
 */
public class HaffNode {
	int weight; // 权值
	int flag; // 标志(是否叶子节点)
	int parent; // 双亲节点下标
	int leftChild; // 左孩子下标
	int rightChild; // 右孩子下标

	public HaffNode() {
	}

	@Override
	public String toString() {
		return "HaffNode [weight=" + weight + ", flag=" + flag + ", parent=" + parent + ", leftChild=" + leftChild
				+ ", rightChild=" + rightChild + "]";
	}
}

2、哈夫曼编码类
示例数组[1, 3, 5, 7]构成哈夫曼树
哈夫曼编码,从根节点到叶子结点,左结点为0,右结点为1;
数组哈夫曼编码分别为:0100, 0101, 0011, 0000

import java.util.Arrays;

/**
 * 哈夫曼编码
 * 
 * @author kiki 2018年11月26日
 */
public class HaffmanCode {

	int[] bit; // 编码数组
	int start; // 编码起始下标
	int weight; // 字符权值

	public HaffmanCode(int n) {
		bit = new int[n];
		start = n - 1;
	}

	@Override
	public String toString() {
		return "HaffmanCode [bit=" + Arrays.toString(bit) + ", start=" + start + ", weight=" + weight + "]";
	}
}

3、哈夫曼树构建类

/**
 * 哈夫曼树和哈夫曼编码
 * @author kiki 2018年11月27日
 */
public class HaffmanTree {
	static final int MAX_VALUE = 1000; // 最大权值
	static int nodeNum; // 叶子结点数量
	static int totalNum; // 结点总数

	public HaffmanTree(int n) {
		nodeNum = n;
		totalNum = 2 * n - 1;
	}

	public static void main(String[] args) {
		int[] values = { 1, 3, 5, 7 }; // 叶子节点
		int n = values.length;
		HaffmanTree haffmanTree = new HaffmanTree(n);
		// 初始化哈夫曼树结点
		HaffNode[] nodes = new HaffNode[totalNum];
		haffmanTree.buildHaffmanTree(values, nodes);
		// 初始化哈夫曼编码
		HaffmanCode[] codes = new HaffmanCode[nodeNum];
		haffmanTree.buildHaffmanCode(nodes, codes);
		for (int i = 0; i < totalNum; i++) {
			System.out.println("weight: " + nodes[i]);
			if (i < nodeNum) {
				System.out.print(" | code: " + codes[i]);
			}
			System.out.println();
		}
	}

	/**
	 * 构建哈夫曼树
	 * 
	 * @param values
	 * @param nodes
	 */
	private void buildHaffmanTree(int[] values, HaffNode[] nodes) {
		// 初始化结点数组
		for (int i = 0; i < totalNum; i++) {
			HaffNode node = new HaffNode();
			if (i < nodeNum) { // 叶子节点
				node.weight = values[i];
			} else {
				node.weight = 0;
			}
			node.flag = 0;
			node.parent = -1;
			node.leftChild = -1;
			node.rightChild = -1;
			nodes[i] = node;
		}
		// 遍历数组,比较出最小的两个结点,构成树
		/*
		 * i=0;j<4 i=1;j<5 i=2;j<6 i=3;j<7
		 */
		// 非叶子结点数量nodeNum-1
		// 构造非叶子结点过程
		for (int i = 0; i < nodeNum - 1; i++) {
			int value1 = MAX_VALUE;// 最小树权值
			int value2 = MAX_VALUE;// 次小树权值
			int index1 = 0; // 最小树索引
			int index2 = 0; // 次小树索引
			int tempTotalNum = nodeNum + i;
			for (int j = 0; j < tempTotalNum; j++) {
				if (nodes[j].weight < value1 && nodes[j].flag == 0) {
					// 如果匹配值比最小结点小,将当前最小树变为次小树
					value2 = value1;
					index2 = index1;
					// 将匹配值变为最小树
					value1 = nodes[j].weight;
					index1 = j;
				} else if (nodes[j].weight < value2 && nodes[j].flag == 0) {
					value2 = nodes[j].weight;
					index2 = j;
				}
			}
			// 父结点权值为子结点权值加和
			nodes[tempTotalNum].weight = nodes[index1].weight + nodes[index2].weight;
			nodes[tempTotalNum].leftChild = index1;
			nodes[tempTotalNum].rightChild = index2;
			// 最小树结点
			nodes[index1].flag = 1; // 已标记
			nodes[index1].parent = tempTotalNum;
			// 次小树结点
			nodes[index2].flag = 1; // 已标记
			nodes[index2].parent = tempTotalNum;
		}
	}

	/**
	 * 构建哈夫曼编码
	 * 
	 * @param nodes
	 * @param codes
	 */
	private void buildHaffmanCode(HaffNode[] nodes, HaffmanCode[] codes) {

		// 由哈夫曼树构造哈夫曼编码
		int n = nodeNum;
		HaffmanCode code = new HaffmanCode(n);
		int child, parent;
		// 求n个叶结点的哈夫曼编码
		for (int i = 0; i < n - 1; i++) {
			code.start = n - 1;		// 不等长编码的最后一位为n-1
			code.weight = nodes[i].weight;// 取得编码对应的权值
			child = i;
			parent = nodes[child].parent;

			while (parent != -1) {
				// 由叶结点向上直到根结点循环
				if (nodes[parent].leftChild == child) {
					code.bit[code.start] = 0;// 左孩子结点编码为0
				} else {
					code.bit[code.start] = 1;// 右孩子结点编码1
				}
				code.start--;
				child = parent;
				parent = nodes[child].parent;
			}
			HaffmanCode temp = new HaffmanCode(n);
			// 保存叶结点的编码和不等长编码的起始位
			for (int j = code.start + 1; j < n; j++) {
				temp.bit[j] = code.bit[j];
			}
			temp.start = code.start;
			temp.weight = code.weight;
			codes[i] = temp;
		}
	}

}

运行结果:

HaffNode [weight=1, flag=1, parent=4, leftChild=-1, rightChild=-1]
HaffmanCode [bit=[0, 1, 0, 0], start=0, weight=1]
HaffNode [weight=3, flag=1, parent=4, leftChild=-1, rightChild=-1]
HaffmanCode [bit=[0, 1, 0, 1], start=0, weight=3]
HaffNode [weight=5, flag=1, parent=5, leftChild=-1, rightChild=-1]
HaffmanCode [bit=[0, 0, 1, 1], start=1, weight=5]
HaffNode [weight=7, flag=1, parent=6, leftChild=-1, rightChild=-1]
null
HaffNode [weight=4, flag=1, parent=5, leftChild=0, rightChild=1]

HaffNode [weight=9, flag=1, parent=6, leftChild=4, rightChild=2]

HaffNode [weight=16, flag=0, parent=-1, leftChild=3, rightChild=5]
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值