目录
二叉检索树
定义
代码实现
二叉树的平衡
优先队列
概述
实现
本文需要对树结构有个基础的认识,如有需要可以看
哈夫曼编码树
引用:3.3哈夫曼编码树 (xjtuse-guide.github.io)
基本定义
路径长度:两个结点之间路径上的分支数
树的外部路径长度:各叶结点到根结点的路径长度之和
树的内部路径长度:各非叶结点到根结点的路径长度之和
树的带权路径长度:树中所有叶子结点的带权路径长度之和
说的通俗一些,就是我们对树中的元素访问的频率不同,比如字母a用的多,v用的少,这个使用频率就是一个权值,访问频率大的元素,我们更想把它放在“浅”的地方,而很少访问的元素,我们就可以把它放在“深”一点的地方,也就是路径长度大的地方,用加权计算可以得到左后的带权路径长度。
而我们的哈夫曼编码树就是希望通过计算机生成一个最优排布,让树的带权路径长度最短,相对来说访问效率也就最快
代码分析
eg.访问速度的缩短比较显然,下面的例子体现哈夫曼树的其他优势
利用Huffman树的特性为使用频率不同的字符编写不等长的编码,从而缩短整个文件的长度
This is isinglass
■ t的频度是1 h的频度是1 i的频度是4 a的频度是1
s的频度是5 n的频度是1 g的频度是1 I的频度是1
如果采用等长的编码形式,上面的八个字母则需要三位二进制编码 长度=15*3=45
按照上面字母出现的频度创建一个Huffman树
我们需要完成两件事,第一是定义二叉树的节点信息(包含权值),第二则是构建哈夫曼树
构建节点数据类型,letter和HuffTreeNode类放一起,难度相对不高
class Letter { char element;//字母 double weight;//字母出现的频率 public Letter(char element, double weight) { this.element = element; this.weight = weight; } //get和set一类的省略了 } class HuffTreeNode { Letter letter; HuffTreeNode left;//左子结点 HuffTreeNode right;//右子结点 public Letter getLetter() { return letter;} public void setLetter(Letter letter) { this.letter = letter; } public HuffTreeNode getLeft() { return left; } public void setLeft(HuffTreeNode left) {this.left = left; } public HuffTreeNode getRight() { return right; } public void setRight(HuffTreeNode right) {this.right = right;} }
下面是哈夫曼树的生成过程,用文字描述就是
1.将两个访问频率最小的节点(x,y)挂在一个新节点z下,z的访问频率等于二者之和
2.将z加入排序中,和其他的节点放在一起(剔除x,y),重复1,2过程
3.当结点序列中只剩下一个结点,它的访问频率是100%,这就是树的root。
public class HuffmanTree { //简单使用冒泡排序 private void sort(HuffTreeNode[] nodes) { int flags = 0; for (int i = 0; i < nodes.length-1; i++) { for (int j = 0; j < nodes.length-1-i; j++) { if (nodes[j].letter.weight > nodes[j + 1].letter.weight) { HuffTreeNode temp = nodes[j]; nodes[j] = nodes[j + 1]; nodes[j + 1] = temp; flags = 1;//不是有序的,flags设置为1; } } if (flags == 0) return; } } public HuffTreeNode generateHuffTree(Letter[] letters) { HuffTreeNode[] nodes = new HuffTreeNode[letters.length]; for (int i = 0; i < letters.length; i++) { nodes[i] = new HuffTreeNode(); nodes[i].letter = letters[i]; }//初始化数组 while (nodes.length > 1) { sort(nodes); HuffTreeNode node1 = nodes[0]; HuffTreeNode node2 = nodes[1]; HuffTreeNode newTree = new HuffTreeNode(); Letter temp = new Letter('0',node1.getLetter().getWeight()+node2.getLetter().getWeight()); newTree.setLetter(temp); newTree.setLeft(node1); newTree.setRight(node2); //完成一次子树生成 HuffTreeNode[] nodes2 = new HuffTreeNode[nodes.length - 1];//新的结点数组,长度减一 for (int i = 2; i < nodes.length; i++) { nodes2[i - 2] = nodes[i]; } nodes2[nodes2.length - 1] = newTree; //新的结点数组初始化完成 nodes = nodes2; } return nodes[0]; } //下面还有部分
截止到这里,我们已经完成了对哈夫曼树的构建和生成,下面将用一种输出方法将结果更好的表现出来,而这种输出方法也更好的说明了哈夫曼树体现出优势的核心原因
public void print(HuffTreeNode root,String code){ if(root != null) { print(root.getLeft(),code+"0"); print(root.getRight(),code+"1"); if(root.getLeft() == null && root.getRight() == null) { String m=root.getLetter().getElement()+"频数:"+root.getLetter().getWeight()+" 哈夫曼编码:"+code; System.out.println(m); } } } public static void main(String[] args) { Letter a = new Letter('a', 1); Letter g = new Letter('g', 1); Letter h = new Letter('h', 1); Letter l = new Letter('l', 1); Letter n = new Letter('n', 1); Letter t = new Letter('t', 1);