常见应用:压缩,最基本的压缩编码的方法,使得总体的编码长度缩短,减少不必要的空间。
什么可以称为哈夫曼树?
公式判断: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链表,快排方法构建出来的哈夫曼树,之后不知道怎么来求编码,无奈-.-!,最后还是回归到了数组。
能力有限,如果哪位同学知道,望告知一下。