哈夫曼树的实现


  1.编码思想

    哈夫曼编码是一种变长的编码方案,字符的编码根据使用频率的的不同而长短不一, 使用频率高的字符其编码较短,使用频率低的字符编码较长,从而使所有的编码总长度为最短.

  1. 统计原始数据中个新号符号的频率,安频率高低的次序排列
  2. 将两个频率最小的相加,作为原本两个节点的父节点,次父节点的频率为子节点之和
  3. 重复上述两部,直到和为只剩下一个元素,那么这个元素就是根

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)相关推荐

  1. 哈弗曼树的带权路径长度

    最近刷题刷到了这一题,此题是北邮往年复试题,看了一些网上的讲解,大多数是方法比较复杂,有些巧妙的方法又往往却缺少解释,为了方便大家理解,给小伙伴们梳理梳理 题目描述: 哈夫曼树,第一行输入一个数n,表 ...

  2. c语言文件压缩与解压缩实验报告,哈弗曼树的文件压缩和解压实验报告(C语言).doc...

    Lab05 树结构的应用 学号: 姓名: 实验时间:2011.5.24 1.问题描述 哈弗曼树的编码与译码 - 功能:实现对任何类型文件的压缩与解码 - 输入:源文件,压缩文件 - 输出:解码正确性判 ...

  3. Python 数据结构与算法 —— 哈弗曼树

    1. 从扩充二叉树到哈弗曼树 扩充二叉树:对二叉树 TT,加入足够多的新叶节点(而不是任意),使 TT 的原有结点都变成度数为 2 的分支节点,得到的二叉树称为 TT 的扩充二叉树. 对于扩充二叉树而 ...

  4. 【DSA】树-哈弗曼树详解(3)

    什么是哈弗曼树 百度百科的定义 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长 ...

  5. 【哈弗曼树】 WOJ2343 围栏维修

    [描述] 农民 John 希望修复围绕农场的一小段围栏.他测量了一下,发现需要N (1 <= N <= 20,000) 根木头,每根都有某一个整数长度 Li (1 <= Li < ...

  6. java实践源码--哈弗曼树

    package huffmanTree; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; ...

  7. 基于最小优先级队列构造哈夫曼树 Java

    哈夫曼编码是一种前缀编码,也就是说,它编码的字符,任何一个字符的编码都不是另一个字符的前缀,这使得对哈夫曼编码进行解码变得容易.而使得哈夫曼编码是前缀编码的关键就是哈夫曼树.哈夫曼树也正是本文要说的. ...

  8. xdoj 1144 K叉哈弗曼树

    n个节点k叉.(如果(n-1)%(k-1)!=0)先合并(n-1)%(k-1)+1个节点再k个K个合并 #include<bits/stdc++.h> using namespace st ...

  9. 【Java数据结构】赫夫曼树

    哈弗曼树 哈弗曼树定义 哈弗曼树示例 哈弗曼树代码实现 哈弗曼树定义 给定 N 个权值作为 N 个叶子结点,构造一棵二叉树,若该树的带权路径长度(WPL)达到最小,称这样的二叉树为最优二叉树,也称为哈 ...

最新文章

  1. 方法 注释_注释模板导入操作方法
  2. Linux中vi编辑器的使用详解
  3. Oralce 数据库 - 查询数据库所有的表和视图实例演示,查询指定用户下所有表和视图方法
  4. hdu-Calculation 2(欧拉函数)
  5. Java获取指定日期的月初和月末日子
  6. JavaFX UI控件教程(二十六)之Pagination Control
  7. ERROR 2384 — [ main] o.s.boot.SpringApplication : Application run failed
  8. Struts2 Hibernate集成示例教程
  9. 超大超详细图解,让你掌握Spark memeoryStore内存管理的精髓
  10. 医疗搜索中的query词权重算法探索
  11. ubuntu安装sasl失败 - 解决方法
  12. 房屋租赁管理系统mysql(含论文)
  13. STM32国产替代,再来一波
  14. 记一次js调试(attachEvent, onchange, onpropertychange)
  15. 用PHP写APP后台
  16. 判断字符串是否是邮箱或者手机号码格式
  17. dynamic动态添加属性
  18. 有什么软件可以连接到linux系统升级,linux系统和应用程序升级方法
  19. 采用MCaaS模式 SAP为有孚网络提供基于HANA的解决方案及服务
  20. python另存为excel_为什么不能从python代码中“另存为”Excel文件?

热门文章

  1. DELPHI参数几个概念上的区别 收藏
  2. 利用信号进行进程之间的通信
  3. python之turtle库(画图)
  4. 一文带你弄懂普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法
  5. win7下不能使用dnw烧写的解决办法——韦东山嵌入式Linux学习笔记05
  6. 处理器在实施任务切换时的操作——《x86汇编语言:从实模式到保护模式》读书笔记39
  7. Source Insight 4怎么取消函数结束提示字符
  8. 操作系统(二)操作系统的四个特征
  9. 计算机设备安全检查表,信息安全检查表
  10. linux中wps默认安装目录,centos6.5 安装wps linux