霍夫曼编码和解码
来源:互联网 发布:淳萃洗发水 知乎 编辑:程序博客网 时间:2024/06/07 11:00
编码步骤:
1、准备待编码的字符串(可以从文本文件中读取不包含中文)。
2、统计字符串中每个字符出现的次数。(设置一个长度为256的数组,使用字符对应的ASCII码作为数组下标,保存次数。如:array[‘a’]=10;表示字符a出现10次。)
3、根据上面的数组,生成节点。(每个字符对应一个节点,以链表形式链接起来,同时链表按从小到大排序。)
4、构造霍夫曼树。每次删除链表中的两个节点,生成一个新节点。并将这个节点重新插入到链表合适位置。(构造霍夫曼树参考:打开)。霍夫曼树解码时需要使用。
5、通过前序遍历,求出每个字符的二进制编码。同样设置一个长度为256的数组,下标为字符对应的ASCII码。没出现的字符编码为null,不考虑。
6、根据求出的二进制编码替换原来的每个字符。得到整个字符串对应的二进制编码。
7、将二进制编码按照每8位生成一个新字符。最后剩的不足8位的在后面补上count个0,计算一个新字符。补0的个数解码时需要使用。
8、将这些生成的新字符替换掉二进制编码字符串,即可得到编码后的内容。长度将在一定程度上变短。
public class Node { // 既是树的节点也是链表的节点 public int data; // 存的字符0-255 public int weight; public Node lchild; public Node rchild; public Node next; // 用于链表 public Node(int d, int w) { data = d; weight = w; lchild = rchild = next = null; }}
public class HuffmanCode { public static int count = 0; public static Node head = null; public static void main(String[] args) { String t = code("youareagoodboy"); String r = decode(t); System.out.println(r); } // 对任意字符串进行霍夫曼编码 public static String code(String str) { // 根据字符串中不同字符出现次数作为权值构造霍夫曼树 // 根据霍夫曼树求出每个字符的对应编码 // 根据编码将原字符串生成编码串 int[] n = new int[256]; // 使用ASCII码确定每个字符出现的次数 int i; for(i=0; i<256; i++) { n[i] = 0; } int len = str.length(); char c; for(i=0; i<len; i++) { // 求次数 c = str.charAt(i); n[c]++; } // 构造一个单向链表,从小到大顺序排列森林中的树 Node t, p, pp; for(i=0; i<256; i++) { if(n[i] != 0) { //(char)i必须出现过 t = new Node(i, n[i]); if(head == null) { head = t; } else { if(t.weight < head.weight) { // 新节点插在首位 t.next = head; // 该节点指向头节点 head = t; // 头指针指向它 } else { // 按顺序加入链表 pp = head; p = head.next; while(p != null) { // 新节点权值较小,放在旧节点前面 if(t.weight < p.weight) { pp.next = t; t.next = p; break; } else { pp = p; p = p.next; } } // 在结尾插入 if(p == null && pp != null) { pp.next = t; } } } } } // 根据这个单向链表森林,构造霍夫曼树 Node min1, min2; Node newNode; while(head.next != null) { // 当森林有超过一棵树时 min1 = head; min2 = head.next; newNode = new Node(256, min1.weight+min2.weight); newNode.rchild = min2; newNode.lchild = min1; // 删除原来两棵树 head = min2.next; // 将新树加进去 if(head == null) { head = newNode; } else { if(newNode.weight < head.weight) { // 新节点插在首位 newNode.next = head; // 该节点指向头节点 head = newNode; // 头指针指向它 } else { // 按顺序加入链表 pp = head; p = head.next; while(p != null) { if(newNode.weight < p.weight) { // 新节点权值较小,放在旧节点前面 pp.next = newNode; newNode.next = p; break; } else { pp = p; p = p.next; } } // 在结尾插入 if(p == null && pp != null) { pp.next = newNode; } } } } // 霍夫曼树构造完毕,求出对应编码,head为树根节点 String[] bianma = new String[256]; for(i=0; i<256; i++) { bianma[i] = null; } preOrder(head, bianma, ""); // 使用编码替换原来的文本 String result = ""; for(i=0; i<len; i++) { result += bianma[str.charAt(i)]; } // 将编码每8位转化成一个ASCII码字符 String s; int m = 1; int j; int r; String real = ""; while(result.length() >= 8) { r = 0; m = 1; s = result.substring(0, 8); result = result.substring(8); for(j=7; j>=0; j--) { r += (s.charAt(j)-'0')*m; m *= 2; } real += (char)r; } count = 8 - result.length(); // 补0的个数 for(j=0; j<count; j++) { result += '0'; } r = 0; m = 1; for(j=7; j>=0; j--) { r += (result.charAt(j)-'0')*m; m *= 2; } real += (char)r; return real; } public static void preOrder(Node head, String[] bianma, String str) { if(head.rchild == null && head.lchild == null) { bianma[head.data] = str; return; } str += "0"; preOrder(head.lchild, bianma, str); str = str.substring(0, str.length() - 1); str += "1"; preOrder(head.rchild, bianma, str); str = str.substring(0, str.length() - 1); } // 根据霍夫曼树对给定字符串解码 public static String decode(String str) { char[] c = str.toCharArray(); String result = ""; String b; int t; // 将每个字符转化成8位二进制串 for(int i=0; i<c.length; i++) { b = ""; t = c[i]; while(t != 0) { b = (t%2) + b; t /= 2; } while(b.length() < 8) { // 不够8位补零 b = '0' + b; } result += b; // 原来的二进制串 } int len = result.length()-count; // 真实二进制字符串长度需要去除补得0 String real = ""; Node p = head; for(int i=0; i<len; i++) { if(p.data == 256) { if(result.charAt(i) == '0') { p = p.lchild; } else { p = p.rchild; } if(p.data != 256) { // 走到叶子点,找到对应字符 real += (char)p.data; p = head; } } } return real; }}
注意:本代码对ASCII码内字符适用。因为其他字符(无论采用哪种编码)超过一个字节,计数和统计编码的数组下标将越界。
0 0
- 霍夫曼编码和解码
- base64编码和解码
- uuencode编码和解码
- BASE64编码和解码
- base64编码和解码
- base64编码和解码
- BASE64 编码和解码
- URL编码和解码
- NIO编码和解码
- 编码和解码问题
- Base64编码和解码
- 曼切斯特差分编码和解码
- BASE64编码和解码
- JavaScript编码 和 解码
- 编码和解码
- url编码和解码
- base64编码和解码
- url编码和解码
- ZOJ 3780 - Paint the Grid Again
- jQuery源码解读一
- Unity 单例与实例的区别
- CentOS vim插件——Doxygen注释插件(用命令生成各种注释)
- FOJ Problem 2214 Knapsack problem
- 霍夫曼编码和解码
- 1094. The Largest Generation (25)
- xpath对XML进行模糊查询
- StringUtils中 isNotEmpty 和isNotBlank的区别
- 警告:receiver Exported receiver does not require permission
- 利用power_profile.xml获取Android运行时的能耗
- 屏幕尺寸/机型/系统版本判定
- Navicat Premium 无法新建查询问题
- MFC处理bmp图像—Otsu阈值分割