哈夫曼树的构建及其编码

常见应用:压缩,最基本的压缩编码的方法,使得总体的编码长度缩短,减少不必要的空间。

什么可以称为哈夫曼树?

公式判断:WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln)

Wi : 表示第i个叶子的节点权值

Li:表示第i个叶子节点到根节点的路径长度

路径长度:通俗点,就是叶子节点到根节点的线段条数

带权路径WPL最小的二叉树就叫做哈夫曼树,也叫最优二叉树。


哈夫曼树构建的步骤:

① 给定n个权值{w1,w2,w3,...,wn}构建只有一个节点的二叉树,从而得到n棵二叉树的集合 F = {T1,T2,T3,...,Tn}

② 选取最小、次小的二叉树分别做左子树和右子树,构建成一棵新的二叉树

③ 删除最小,次小的二叉树,将新的二叉树加入F中

④ 重复② ③ ,知道只剩下一个二叉树为止

哈夫曼树的构建,编码的实现代码

//节点对象
public class Node {
	String data;    //节点标识符
	double weight;
	boolean flag;	//标记是否被使用
	Node leftChild;
	Node rightChild;
	Node parent;
	
	public Node(String data,double weight){
		this.data = data;
		this.weight = weight;
		this.flag = false;
		this.leftChild = null;
		this.rightChild = null;
		this.parent = null;
	}
	
	public Node(String data, double weight, boolean flag, Node leftChild,
			Node rightChild, Node parent) {
		super();
		this.data = data;
		this.weight = weight;
		this.flag = flag;
		this.leftChild = leftChild;
		this.rightChild = rightChild;
		this.parent = parent;
	}

	@Override
	public String toString() {
		return "Node [data=" + data + ", weight=" + weight + "]";
	}
}
/**
 * @author Robert
 */
public class HaffumaTree {
	public static final char BASE = 'A';
	//哈夫曼树构建
	public static Node[] createTree(double[] in){
		int m = in.length*2 - 1;    //第i层有n个叶子节点,那么总的节点最多有2n-1(可以理解为最后一层的节点-1,就是这棵树除这一层,之前层所有节点之和)
		Node[] nodes = new Node[m + 1];
		//初始化结点数组
		for(int i=0;i<in.length;i++) {
			nodes[i] = new Node(String.valueOf(BASE)+i,in[i]);
		}
		for(int i=in.length;i<m;i++) {
			Node firstMin = getMinWeight(nodes, i-1);     //最小
			firstMin.flag = true;
			Node secondMin = getMinWeight(nodes, i-1);    //次小
			secondMin.flag = true;
			nodes[i] = new Node(String.valueOf(i + BASE),firstMin.weight+secondMin.weight); //父节点构建
			nodes[i].leftChild = firstMin;
			nodes[i].rightChild = secondMin;
			firstMin.parent = nodes[i];
			secondMin.parent = nodes[i];
		}
		return nodes;
	}
	
	//查找最小值,也可以使用先排序,然后直接选取前两个元素。
	public static Node getMinWeight(Node[] nodes,int endIndex){
		Node minNode = nodes[endIndex];
		for(int i=0;i<endIndex;i++) {
			if(!nodes[i].flag && nodes[i].weight<minNode.weight){
				minNode = nodes[i];
			}
		}
		return minNode;
	}
	
	//哈弗曼编码,从叶子结点开始
	public static int[][] haffumanCode(Node[] nodes,int n){
		int[][] haffumanCode = new int[n][n];
		for(int i=0;i<n;i++){
			//不知道有多少位,所以选择逆向存储
			int start = n-1;
			Node now = nodes[i];
			Node pa = now.parent;
			for(;pa!=null;now=pa,pa=pa.parent){
				if(pa.leftChild.equals(now)){
					haffumanCode[i][start--] = 0;
				} else {
					haffumanCode[i][start--] = 1;
				}
			}
			//做结尾的标识,方便后续输出
			haffumanCode[i][start--] = -1;
		}
		return haffumanCode;
	}
	
	public static void main(String[] args) {
		double[] w = {7,5,2,4};
		//创建哈夫曼树
		Node[] tree = createTree(w);
		//得到哈夫曼编码
		int[][] code = haffumanCode(tree, w.length);
		System.out.println("==============");
		for(int i=0;i<w.length;i++) {
			System.out.print(tree[i].data + ":");
			for(int j=0;j<code[i].length;j++) {
				if(code[i][j] == -1) {
					for(int k=j+1;k<code[i].length;k++) {
						System.out.print(code[i][k]);
					}
					break;
				}
			}
			System.out.println();
		}
	}
}

总结

① 在现实生活中,有时候哈夫曼树未必比某些二叉树要有优势。 

② 自己用List链表,快排方法构建出来的哈夫曼树,之后不知道怎么来求编码,无奈-.-!,最后还是回归到了数组。

能力有限,如果哪位同学知道,望告知一下。


猜你喜欢

转载自blog.csdn.net/YCJLXDLB/article/details/80286004