简单哈弗曼树(Java)
哈夫曼树的实现
1.编码思想
哈夫曼编码是一种变长的编码方案,字符的编码根据使用频率的的不同而长短不一, 使用频率高的字符其编码较短,使用频率低的字符编码较长,从而使所有的编码总长度为最短.
- 统计原始数据中个新号符号的频率,安频率高低的次序排列
- 将两个频率最小的相加,作为原本两个节点的父节点,次父节点的频率为子节点之和
- 重复上述两部,直到和为只剩下一个元素,那么这个元素就是根
2.解码思想
利用Haffman树进行解码,已知一个二进制位串S,
从串S的第一位出发,逐位的去匹配二叉树边上标记的0和1
从haffman树的根节点出发,遇到0时,向左,遇到1时向右,若干连续的0和1确定一条从根节点到某个叶子节点的路径.一旦到达一个叶子节点,便译出一个字符,
接着从S的下一个位开始继续寻找,然后在重新将指针指向根.
编码和解码操作
主要有三部分
1.对编码进行计数,然后通过键值对构造haffman树,这里主要使用了优先队列,也就是最小堆,需要内部节点类实现Comparable接口
2.通过树,返回叶节点的编码,这里我利用Map<Character, String>这样将字符和编码绑定到了一起,方便后面操作
3.根据输入的字符串,遍历,返回编码后的字符串,如下:
//toCode()返回一个Map键值对Map<Character, String> codes = haff.toCode() ;StringBuilder stringcode = new StringBuilder() ;//遍历字符串,求其编码for(char ch : text.toCharArray()) {stringcode.append(codes.get(ch)) ;}return stringcode.toString() ;
下面是代码演示:
1 package com.jffx.util; 2 3 import java.util.*; 4 5 /* 6 本类负责逻辑处理, 7 负责串---->码的编码 8 吗---->串的解码 9 */ 10 public class HaffmanCode { 11 12 private Node root ; 13 14 15 //---------------------------------------- 下面是内部节点类 16 static class Node implements Comparable<Node> { 17 Integer weight ; 18 String code = "" ; //取一个字符串域表示每个节点的编码 19 Character data ; 20 21 Node parent ; 22 Node left ; 23 Node right ; 24 public Node(int w, Node left, Node right, Node parent) { 25 this.weight = w ; 26 this.left = left ; 27 this.parent = parent ; 28 this.right = right ; 29 } 30 31 @Override 32 public int compareTo(Node o) { 33 return this.weight - o.weight ; 34 } 35 36 @Override 37 public String toString() { 38 return "[" + this.data + ", " + this.weight + "]" ; 39 } 40 } 41 42 43 /** 44 * 构造haffman树 45 * 先将strs这样的键值对转化为节点, 然后加入优先队列 46 * @param strs 字符--频率这样的键值对 47 */ 48 public HaffmanCode(Map<Character, Integer> strs) { 49 Queue<Node> queue = new PriorityQueue<>() ; 50 51 Set<Character> keys = strs.keySet() ; 52 for(Character key : keys) { 53 Node newNode = new Node(strs.get(key), null, null, null) ; 54 newNode.data = key ; 55 newNode.weight = strs.get(key) ; 56 57 //入堆 -- 覆写CompareTo 58 queue.add(newNode) ; 59 } 60 61 while(queue.size() > 1) { 62 Node n1 = queue.poll() ; 63 Node n2 = queue.poll() ; 64 65 Node sumNode = new Node(n1.weight + n2.weight , n1, n2, null) ; 66 67 n1.parent = sumNode ; 68 n2.parent = sumNode ; 69 70 queue.add(sumNode) ; 71 } 72 73 this.root = queue.poll() ; 74 } 75 76 /** 77 * 将树的叶节点的编码返回, 返回的键值是 字符 -- 编码 这样的形式 78 * @return 79 */ 80 public Map<Character, String> toCode() { 81 Map<Character, String> map = new HashMap<>() ; 82 83 preTraverse() ; 84 85 //前序遍历.将叶节点的属性保存到map中 86 List<Node> stack = new LinkedList<>() ; 87 Node p = this.root ; 88 while(p != null || !stack.isEmpty()) { 89 while(p != null) { 90 stack.add(p) ; 91 p = p.left ; 92 } 93 //如果栈不空,去栈顶元素,然后向右 94 if(!stack.isEmpty()) { 95 p = ((LinkedList<Node>) stack).pop() ; 96 if(isLeafNode(p)) { 97 map.put(p.data, p.code) ; 98 } 99 p = p.right ; 100 } 101 } 102 return map ; 103 } 104 105 /** 106 * 输入一串编码,然后返回解码后的字符串 107 * @return 108 */ 109 public String deCode(String codes) { 110 StringBuilder stringBuilder = new StringBuilder() ; 111 112 Node p = this.root ; //从根出发 113 for(int i = 0 ; i < codes.length() ; ++i) { 114 if(codes.charAt(i) == '0') { 115 p = p.left ; 116 } else { 117 p = p.right ; 118 } 119 if(isLeafNode(p)) { //如果是叶子 120 stringBuilder.append(p.data) ; 121 p = this.root ; 122 } 123 } 124 return stringBuilder.toString() ; 125 } 126 127 private void preTraverse() { 128 preTraverse(this.root) ; 129 } 130 private void preTraverse(Node root) { 131 if(root != null) { 132 if(root != this.root) { //如果不是根节点 133 if(root == root.parent.left) { //左孩子 134 root.code = root.parent.code + "0" ; 135 } else { 136 root.code = root.parent.code + "1" ; 137 } 138 } 139 //System.out.println(root) ; 140 preTraverse(root.left) ; 141 preTraverse(root.right) ; 142 } 143 } 144 145 public boolean isLeafNode(Node node) { 146 return node.left == null && node.right == null ; 147 } 148 149 150 151 152 153 154 155 156 157 /** 158 * 测试 159 * @param args 160 */ 161 public static void main(String[] args) { 162 String str = "today is Saturday." ; 163 164 char[] strCh = str.toCharArray() ; 165 Map<Character, Integer> map = new HashMap<>() ; 166 167 for(int i = 0 ; i < strCh.length ; ++i) { 168 if(map.containsKey(strCh[i])) { //如果存在键 169 map.put(strCh[i], map.get(strCh[i]) + 1) ; 170 } else { 171 map.put(strCh[i], 1) ; 172 } 173 } 174 175 HaffmanCode haff = new HaffmanCode(map) ; 176 177 String code = getTextCode(haff, str) ; 178 String decode = haff.deCode(code) ; 179 System.out.println(decode) ; 180 } 181 182 /** 183 * 获取一个字符串的编码 184 * @param text 需要压缩的字符串 185 * @return 186 */ 187 public static String getTextCode(HaffmanCode haff, String text) { 188 189 Map<Character, String> codes = haff.toCode() ; 190 StringBuilder stringcode = new StringBuilder() ; 191 for(char ch : text.toCharArray()) { 192 stringcode.append(codes.get(ch)) ; 193 } 194 return stringcode.toString() ; 195 } 196 }
这里你会发现,你压缩后的数据比你没压缩前还大,额--------
学无止境
转载于:https://www.cnblogs.com/jffx/p/9997868.html
简单哈弗曼树(Java)相关推荐
- 哈弗曼树的带权路径长度
最近刷题刷到了这一题,此题是北邮往年复试题,看了一些网上的讲解,大多数是方法比较复杂,有些巧妙的方法又往往却缺少解释,为了方便大家理解,给小伙伴们梳理梳理 题目描述: 哈夫曼树,第一行输入一个数n,表 ...
- c语言文件压缩与解压缩实验报告,哈弗曼树的文件压缩和解压实验报告(C语言).doc...
Lab05 树结构的应用 学号: 姓名: 实验时间:2011.5.24 1.问题描述 哈弗曼树的编码与译码 - 功能:实现对任何类型文件的压缩与解码 - 输入:源文件,压缩文件 - 输出:解码正确性判 ...
- Python 数据结构与算法 —— 哈弗曼树
1. 从扩充二叉树到哈弗曼树 扩充二叉树:对二叉树 TT,加入足够多的新叶节点(而不是任意),使 TT 的原有结点都变成度数为 2 的分支节点,得到的二叉树称为 TT 的扩充二叉树. 对于扩充二叉树而 ...
- 【DSA】树-哈弗曼树详解(3)
什么是哈弗曼树 百度百科的定义 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长 ...
- 【哈弗曼树】 WOJ2343 围栏维修
[描述] 农民 John 希望修复围绕农场的一小段围栏.他测量了一下,发现需要N (1 <= N <= 20,000) 根木头,每根都有某一个整数长度 Li (1 <= Li < ...
- java实践源码--哈弗曼树
package huffmanTree; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; ...
- 基于最小优先级队列构造哈夫曼树 Java
哈夫曼编码是一种前缀编码,也就是说,它编码的字符,任何一个字符的编码都不是另一个字符的前缀,这使得对哈夫曼编码进行解码变得容易.而使得哈夫曼编码是前缀编码的关键就是哈夫曼树.哈夫曼树也正是本文要说的. ...
- xdoj 1144 K叉哈弗曼树
n个节点k叉.(如果(n-1)%(k-1)!=0)先合并(n-1)%(k-1)+1个节点再k个K个合并 #include<bits/stdc++.h> using namespace st ...
- 【Java数据结构】赫夫曼树
哈弗曼树 哈弗曼树定义 哈弗曼树示例 哈弗曼树代码实现 哈弗曼树定义 给定 N 个权值作为 N 个叶子结点,构造一棵二叉树,若该树的带权路径长度(WPL)达到最小,称这样的二叉树为最优二叉树,也称为哈 ...
最新文章
- 方法 注释_注释模板导入操作方法
- Linux中vi编辑器的使用详解
- Oralce 数据库 - 查询数据库所有的表和视图实例演示,查询指定用户下所有表和视图方法
- hdu-Calculation 2(欧拉函数)
- Java获取指定日期的月初和月末日子
- JavaFX UI控件教程(二十六)之Pagination Control
- ERROR 2384 — [ main] o.s.boot.SpringApplication : Application run failed
- Struts2 Hibernate集成示例教程
- 超大超详细图解,让你掌握Spark memeoryStore内存管理的精髓
- 医疗搜索中的query词权重算法探索
- ubuntu安装sasl失败 - 解决方法
- 房屋租赁管理系统mysql(含论文)
- STM32国产替代,再来一波
- 记一次js调试(attachEvent, onchange, onpropertychange)
- 用PHP写APP后台
- 判断字符串是否是邮箱或者手机号码格式
- dynamic动态添加属性
- 有什么软件可以连接到linux系统升级,linux系统和应用程序升级方法
- 采用MCaaS模式 SAP为有孚网络提供基于HANA的解决方案及服务
- python另存为excel_为什么不能从python代码中“另存为”Excel文件?
热门文章
- DELPHI参数几个概念上的区别 收藏
- 利用信号进行进程之间的通信
- python之turtle库(画图)
- 一文带你弄懂普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法
- win7下不能使用dnw烧写的解决办法——韦东山嵌入式Linux学习笔记05
- 处理器在实施任务切换时的操作——《x86汇编语言:从实模式到保护模式》读书笔记39
- Source Insight 4怎么取消函数结束提示字符
- 操作系统(二)操作系统的四个特征
- 计算机设备安全检查表,信息安全检查表
- linux中wps默认安装目录,centos6.5 安装wps linux