本文讲述哈夫曼树使用的全过程。
包括:1、根据条件创建哈夫曼树;2、利用哈夫曼树编码解码字符串。
用到的知识:
顺序表(List)排序
字典
程序构思:
创造一个预设字符类(pre_letter)
属性包括:字母l(letter)和使用率权值w(weight)
静态方法1,返回一个字母和权值的顺序表。(即书上P95例4-3)(P101第10题可用)
静态方法2,根据一个字符串,统计字母和权重,返回顺序表。(即书上P99实战演练1)(P101第10题可用)
------------------------------------------------------------------------------------
创造加解码工具(哈夫曼树是解码工具,字典是编码工具):
创造一个哈夫曼节点类(HafNode)
属性包括:字母(letter)、使用率权值(weight)、左右节点指针(l,r)
方法包括:
构造叶子节点:(pre_letter)
构造衔接节点:()
根据预设字符顺序表,得到哈夫曼树,返回根节点(先对顺序表排序,降低程序复杂度。实际编程中,这一步放在预设字符类的静态方法中完成)。
为哈夫曼树的叶子节点计算哈夫曼编码,并返回一个编码字典:字典<string,string> fill_HfCode()
------------------------------------------------------------------------------------------
使用工具:
方法1:输入一串字符,根据字典,编码。(即书上P99实战演练2)
方法2:输入一串编码,根据哈夫曼树,解码。(即书上P99实战演练3)
java代码:
预设字符类
import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; public class Pre_Letter implements Comparable<Pre_Letter> { String l; int w; public Pre_Letter(String x, int y) { l = x; w = y; } public int compareTo(Pre_Letter obj) { return w - obj.w; } public static ArrayList<Pre_Letter> get_Pre_Letters() { ArrayList<Pre_Letter> x = new ArrayList<>(); // 故意打乱顺序 x.add(new Pre_Letter("A", 1)); x.add(new Pre_Letter("C", 5)); x.add(new Pre_Letter("B", 3)); x.add(new Pre_Letter("D", 7)); x.sort(Comparator.naturalOrder()); //System.out.println("ArrayList<Pre_Letter>[2]="+x.get(2).l); return x; } public static ArrayList<Pre_Letter> get_Pre_Letters(String s) { char y; int c; ArrayList<Pre_Letter> x = new ArrayList<>(); // 字符串排序成字符数组 //即相同字母排在一起,从小到大排列 char[] t = s.toCharArray(); Arrays.sort(t); c=0; y=t[0]; //逐个扫描,统计每个字母出现次数,填入顺序表 for (int i = 0; i < t.length; i++) { if(t[i]==y) { c++; } else { x.add(new Pre_Letter(""+y, c)); y=t[i]; c=1; } } x.add(new Pre_Letter(""+y, c)); x.sort(Comparator.naturalOrder()); return x; } }
哈夫曼节点类(HafNode)
import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class HafNode { String letter; int weight; HafNode l, r; public HafNode() { } public HafNode(Pre_Letter x) { letter = x.l; weight = x.w; } //也可以用构造方法完成 public static HafNode get_Haf(ArrayList<Pre_Letter> xLetters) { HafNode t=null, t1,t2; if (xLetters.size()==0) { return null; } t1 = new HafNode(xLetters.get(0)); for (int i = 1; i < xLetters.size(); i++) { t=new HafNode(); t2=new HafNode(xLetters.get(i)); t.l=t1.weight<t2.weight?t1:t2; t.r=t1.weight>t2.weight?t1:t2; t.weight=t1.weight+t2.weight; t1=t; } return t; } //中序遍历这棵树,并用字符串记录路径 //向左记“0”,向右计“1”。 //遇到叶子节点则写入字典。 public Map<String,String> fill_HfCode() { StringBuilder sb=new StringBuilder(); Map<String,String> a = new HashMap<String,String>(); mid(sb, a); return a; } public void mid(StringBuilder sb1,Map<String,String> map1) { //除叶子节点外,哈夫曼树节点左右子树必定全齐。 //且除叶子节点外,其余节点我们并不关心。 if(l!=null) { sb1.append("0"); l.mid(sb1,map1); sb1.deleteCharAt(sb1.length()-1); //所以这里可以不用管根节点的事情。 sb1.append("1"); r.mid(sb1,map1); sb1.deleteCharAt(sb1.length()-1); } else { map1.put(letter, sb1.toString()); } } //调试用 // public void front()// 递归-前序遍历 // { // System.out.print(letter + "\t"); // if (l != null) // { // l.front(); // } // if (r != null) // { // r.front(); // } // } }
主程序:
import java.util.Map; import java.util.Scanner; public class App { public static Scanner scan = new Scanner(System.in); public static void main(String[] args) throws Exception { HafNode myhaf; Map<String, String> mymap; // 固定内容调试 // 待编码:ABCDBBCCDDCDDDCD // 待解码:10010111010110111110011000110 // myhaf=HafNode.get_Haf(Pre_Letter.get_Pre_Letters()); // 动态内容调试 // 为了满足101页课后第10题的要求,测试内容如下 // 待统计:FFDDFFEEFFCFFBBCDFDDAABDEEEBCCCEEEFFF // 待编码:ABCDEF // 待解码:11110111111110110100 System.out.println("输入生成哈夫曼树的字符串:"); myhaf=HafNode.get_Haf(Pre_Letter.get_Pre_Letters(scan.next())); mymap = myhaf.fill_HfCode(); System.out.println(myEncode(mymap)); System.out.println(myDecode(myhaf)); } public static String myEncode(Map<String, String> map) { String s, t; StringBuilder s1 = new StringBuilder(); System.out.println("Input a string to encode:"); s = scan.next(); for (int i = 0; i < s.length(); i++) { t = s.substring(i, i + 1); s1.append(map.get(t)); } return s1.toString(); } public static String myDecode(HafNode hafnode) { String s, t; StringBuilder s1 = new StringBuilder(); HafNode tHaf; System.out.println("Input a string to decode:"); s = scan.next(); tHaf = hafnode; for (int i = 0; i < s.length(); i++) { t = s.substring(i, i + 1); tHaf = t.equals("0") ? tHaf.l : tHaf.r; while (tHaf.l == null) { s1.append(tHaf.letter); tHaf = hafnode; } } return s1.toString(); } }
静态调试运行结果:
动态调试运行结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2019-11-26 datatable转layui表格v2[分页and带模板]【偏实例】