解码:译码过程是分解、识别各个字符,还原数据的过程。从字符串头开始扫描到尾,依次去匹配。java
首先构造结点类,每一个结点须要有哈夫曼编码,字符自己,权值,以及左结点和右结点。node
public class TreeNode { public String code = "";// 节点的哈夫曼编码 public String data = "";// 节点的字符 public int count;// 节点的权值 public TreeNode lChild;//左结点 public TreeNode rChild;//右结点 public TreeNode(String data, int count) { this.data = data; this.count = count; } public TreeNode(int count, TreeNode lChild, TreeNode rChild) { this.count = count; this.lChild = lChild; this.rChild = rChild; } }
计算每一个字符的权重,flag用来断定取出的字符串是否存在,若不存在为true,若是存在那么就在出现次数统计上加一,即权重加一,若是没有出现过,就将它加入到存储字符的链表中。git
// 统计出现的字符及出现次数 private void getCharNum(String str) { for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); // 从字符串中取出字符 flag = true; for (int j = 0; j < charList.size(); j++) { CharData data = charList.get(j); // 遍历字符链表,如有相同字符则将个数加1 if(ch == data.chardata){ data.num++; flag = false; totalcount++; break; } } // 字符对象链表中没有相同字符则将其加到链表中 if(flag){ charList.add(new CharData(ch)); totalcount++; } } }
将字符链表中的字符构建为结点,利用循环将字符链表中的字符和其权值建立为TreeNode结点,并加入到结点链表中。算法
//将字符建立为结点 private void creatNodes() { for (int i = 0; i < charList.size(); i++) { String data = charList.get(i).chardata + ""; int count = charList.get(i).num;//权值 TreeNode node = new TreeNode(data, count); // 建立节点对象 NodeList.add(node); // 加入到节点链表 } }
构建哈弗曼树,根据上文提到的哈夫曼编码的规则,将最小的两个节点分别设置为左右子结点,左边编码为0,右边为1,而后依据父结点建立新的TreeNode结点,将新的结点放到链表首位并从新排序。一次循环进行,直到结点链表中的节点数不大于1,这时树就造成了。this
//构建哈夫曼树 private void creatTree() { // 当节点数目大于一时,将权值最小的两个节点生成一棵新树,父结点为二者之和,并删除两个结点将新树放到列表中 while (NodeList.size() > 1) { TreeNode left = NodeList.poll(); TreeNode right = NodeList.poll(); // 设置各个结点的哈夫曼编码 left.code = "0"; right.code = "1"; setCode(left); setCode(right); int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和 TreeNode parent = new TreeNode(parentWeight, left, right); NodeList.addFirst(parent); // 暂时将父节点置于首位 Sort(NodeList); // 从新排序将父结点放到合适位置 } }
在构建哈夫曼树的过程当中每次将最小的两个相加构造新结点时,须要对新的链表进行排序,因此用到升序排序法,将链表从左开始依次与其右边的全部字符比较权值,将权值小的字符放到左边,这样就造成升序链表。编码
//升序排序 private void Sort(LinkedList<TreeNode> nodelist) { for (int i = 0; i < nodelist.size() - 1; i++) { for (int j = i + 1; j < nodelist.size(); j++) { TreeNode temp; if (nodelist.get(i).count > nodelist.get(j).count) { temp = nodelist.get(i); nodelist.set(i, nodelist.get(j)); nodelist.set(j, temp); } } } }
设置每一个结点的哈夫曼编码,从根结点开始,分别对作孩子和右孩子的编码添加0/1,左结点为0,右结点为1。这样每一个叶子节点都有独一无二的01编码。.net
//设置结点的哈夫曼编码 private void setCode(TreeNode root) { if (root.lChild != null) { root.lChild.code = root.code + "0"; setCode(root.lChild); } if (root.rChild != null) { root.rChild.code = root.code + "1"; setCode(root.rChild); } }
利用以上方法,根据哈夫曼编码的规则构建哈夫曼树code
//构建哈夫曼树 public void creatHuffmanTree(String str) { this.str = str; NodeList = new LinkedList<TreeNode>(); charList = new LinkedList<CharData>(); // 统计字符串中字符以及字符的出现次数 getCharNum(str); // 建立节点 creatNodes(); // 对节点升序排序 Sort(NodeList); // 将权值最小的两个节点相加生成一个新的父节点并删除权值最小的两个节点,将父节点存放到列表中,造成树 creatTree(); // 将最后的一个节点赋给根节点 root = NodeList.get(0); }
遍历节点输出字符的编码,经过判断左右孩子是否为空的状况,找到叶子结点,即字符,而后输出其编码和出现次数。对象
//遍历结点 private void output(TreeNode node) { if (node.lChild == null && node.rChild == null) { System.out.println(node.data + " 的编码为:" + node.code+ " 出现次数为:"+ node.count + " 出现几率为:"+ (node.count)/totalcount); } if (node.lChild != null) { output(node.lChild); } if (node.rChild != null) { output(node.rChild); } }
编码,定义字符串hfmCodeStr,将叶子结点的编码依次添加到编码字符串中,返回hfmCodeStr字符串。blog
//编码 public String creHufmCode(String str) { for (int i = 0; i < str.length(); i++) { String string = str.charAt(i) + ""; search(root, string); } return hfmCodeStr; } //找到叶子结点,添加其编码 private void search(TreeNode root, String c) { if (root.lChild == null && root.rChild == null) { if (c.equals(root.data)) { hfmCodeStr += root.code; } } if (root.lChild != null) { search(root.lChild, c); } if (root.rChild != null) { search(root.rChild, c); } }
解码,经过遍历叶子结点找到相匹配的字符编码,定义编码后的字符串从第一个数字开始,用substring方法截取字符串,若是找到相匹配的叶子结点编码,就进行解码,当解码失败时,说明编码不匹配,end向后移,当解码成功时,first向后移,对下一段字符进行解码,最后输出解码后的字符串。
//解码 public String decode(String codeStr) { int first = 0; int end = 1; while(end <= codeStr.length()){ target = false; String s = codeStr.substring(first, end); matchCode(root, s); // 解码 // 每解码一个字符,first向后移 if(target){ first = end; } end++; } return result; } //匹配字符哈夫曼编码,找到对应的字符 private void matchCode(TreeNode root, String code){ if (root.lChild == null && root.rChild == null) { if (code.equals(root.code)) { result += root.data; // 找到对应的字符,拼接到解码字符串后 target = true; // 标志为true } } if (root.lChild != null) { matchCode(root.lChild, code); } if (root.rChild != null) { matchCode(root.rChild, code); } }
读取文件中的字符串,并构造哈夫曼树
BufferedReader a = new BufferedReader(new FileReader("wen.txt")); String data = a.readLine(); huff.creatHuffmanTree(data);// 构造树
将编码解码后的内容写入文件
//写入文件 File file = new File("wen2"); Writer write = new FileWriter(file); write.write(hufmCode); write.flush(); write.write(huff.decode(hufmCode)); write.close(
将编码解码后的结果存入文件
https://gitee.com/CS-IMIS-23/20172314/blob/master/src/新第十四周/Huffman.java