下面咱们以【五、八、四、十一、九、13】为例来画出哈夫曼树(数字大小表明权重大小,越大的权重越大)html
【五、八、四、十一、九、13】→【四、五、八、九、十一、13】node
给定的四、五、八、九、十一、13为白色, 红色的9为4+5,与给定的白9无关,新序列为:【红9(含子节点四、5)、八、九、十一、13】
git
排序:
算法
取两个最小数8和9:
数组
排序:
数据结构
取两个最小数9和11:
测试
排序,而后取两个最小数13和17:
优化
取两个最小数20和30:
ui
哈夫曼研究这种最优树的目的是为了解决当年远距离通讯(主要是电报)的数据传输的最优化问题。编码
好比咱们有一段文字“BADCADFEED”,显然用二进制数字(0和1)表示是很天然的想法。
这样真正传输的数据就是“001000011010000011101100100011”,对方接收时一样按照3位一组解码。若是一篇文章很长,这样的二进制串也很是的可怕。并且事实上,每一个字母或者汉子的出现频率是不一样的。
假设六个字母的频率为A 27,B 8, C 15, D 15 , E 30, F 5,合起来正好是100%,那就意味着咱们彻底能够用哈夫曼树来规划它们。
左图为构造哈夫曼树的过程的权值显示。右图为将权值左分支改成0,右分支改成1后的哈夫曼树。
咱们对这六个字母用其从树根到叶子所通过的路径的0或1来编码,能够获得下表:
也就是说咱们的数据被压缩了,节约了大概17%的存储或传输成本。随着字符的增长和多字符权重的不一样,这种压缩会更显出优点来。
哈夫曼编码测试
设有字符集:S={a,b,c,d,e,f,g,h,i,j,k,l,m,n.o.p.q,r,s,t,u,v,w,x,y,z}。
给定一个包含26个英文字母的文件,统计每一个字符出现的几率,根据计算的几率构造一颗哈夫曼树。
并完成对英文文件的编码和解码。
要求:
(1)准备一个包含26个英文字母的英文文件(能够不包含标点符号等),统计各个字符的几率
(2)构造哈夫曼树
(3)对英文文件进行编码,输出一个编码后的文件
(4)对编码文件进行解码,输出一个解码后的文件
(5)撰写博客记录实验的设计和实现过程,并将源代码传到码云
(6)把实验结果截图上传到云班课
满分:6分。
酌情打分。
构造哈夫曼树:
public static HuffmanNode createTree(List<HuffmanNode> nodes) { // 只要nodes数组中还有2个以上的节点 while (nodes.size() > 1) { quickSort(nodes); //获取权值最小的两个节点 HuffmanNode left = nodes.get(nodes.size()-1); left.setCodeNumber(0+""); HuffmanNode right = nodes.get(nodes.size()-2); right.setCodeNumber(1+""); //生成新节点,新节点的权值为两个子节点的权值之和 HuffmanNode parent = new HuffmanNode(null, left.weight + right.weight); //让新节点做为两个权值最小节点的父节点 parent.leftChild = left; parent.rightChild = right; //删除权值最小的两个节点 nodes.remove(nodes.size()-1); nodes.remove(nodes.size()-1); //将新节点加入到集合中 nodes.add(parent); } return nodes.get(0); } /** * 将指定集合中的i和j索引处的元素交换 * * @param nodes * @param i * @param j */ private static void swap(List<HuffmanNode> nodes, int i, int j) { HuffmanNode tmp; tmp = nodes.get(i); nodes.set(i, nodes.get(j)); nodes.set(j, tmp); } /** * 实现快速排序算法,用于对节点进行排序 * @param nodes * @param start * @param end */ private static void subSort(List<HuffmanNode> nodes, int start, int end) { if (start < end) { // 以第一个元素做为分界值 HuffmanNode base = nodes.get(start); // i从左边搜索,搜索大于分界值的元素的索引 int i = start; // j从右边开始搜索,搜索小于分界值的元素的索引 int j = end + 1; while (true) { // 找到大于分界值的元素的索引,或者i已经到了end处 while (i < end && nodes.get(++i).weight >= base.weight); // 找到小于分界值的元素的索引,或者j已经到了start处 while (j > start && nodes.get(--j).weight <= base.weight); if (i < j) { swap(nodes, i, j); } else break; } swap(nodes, start, j); //递归左边子序列 subSort(nodes, start, j - 1); //递归右边子序列 subSort(nodes, j + 1, end); } } public static void quickSort(List<HuffmanNode> nodes) { subSort(nodes, 0, nodes.size()-1); } //层序遍历 public static List<HuffmanNode> levelTraversal(HuffmanNode root) { Queue<HuffmanNode> queue = new ArrayDeque<HuffmanNode>(); List<HuffmanNode> list = new ArrayList<HuffmanNode>(); if(root!=null) { //将根元素加入“队列” queue.offer(root); root.leftChild.setCodeNumber(root.getCodeNumber()+"0"); root.rightChild.setCodeNumber(root.getCodeNumber()+"1"); } while(!queue.isEmpty()) { //将该队列的“队尾”元素加入到list中 list.add(queue.peek()); HuffmanNode tree = queue.poll(); //若是左子节点不为null,将它加入到队列 if(tree.leftChild != null) { queue.offer(tree.leftChild); tree.leftChild.setCodeNumber(tree.getCodeNumber()+"0"); } //若是右子节点不为null,将它加入到队列 if(tree.rightChild != null) { queue.offer(tree.rightChild); tree.rightChild.setCodeNumber(tree.getCodeNumber()+"1"); } } return list; }
计算字母出现次数:
//层序遍历显示构建的哈弗曼树 char[] chars = new char[a]; int[] times = new int[a]; Iterator<Character> pl2 = counter.keySet().iterator(); for (int i=0;i<=a;i++ ) { if (pl2.hasNext()) { chars[i] = pl2.next(); times[i] = counter.get(chars[i]); } } List<HuffmanNode> list = new ArrayList<HuffmanNode>(); for(int i = 0;i<a;i++) { System.out.print(chars[i]+"出现次数为:"+times[i]+" \n"); list.add(new HuffmanNode(chars[i]+"",times[i])); }
读取文件:
File file = new File("E:\\IDES_Project\\JSSD\\src\\HaffmanCoding\\test.txt"); if (!file.exists()) { throw new Exception("文件不存在"); } BufferedReader fin = new BufferedReader(new FileReader(file)); String line; Map<Character, Integer> counter = new HashMap<Character, Integer>(); int total=0; while ((line = fin.readLine()) != null) { int len = line.length(); for (int i = 0; i < len; i++) { char c = line.charAt(i); if (( (c >= 'a' && c <= 'z'&& c == ' '))) { continue; } if (counter.containsKey(c)) { counter.put(c, counter.get(c) + 1); } else { counter.put(c, 1); } } } fin.close();
此次实验给人的感受有点难,不是很懂,因此写起来很迷。