这个老师上课说过。首先肯定文件中各个字符的几率而后将其。按照几率大小。将几率小的放在离根节点较近的地方。
这里用数字来表明字符的几率而后,首先将几率最小的两个字符放到一块儿,将它们的几率合成父结点的几率,而后父结点参与比较再次在备选集中找到比较小的两两结合,而后再次合成父结点的几率。java
由于哈夫曼须要考虑到父结点的影响,因此定义了相关的左孩子右孩子的方法。node
package HuffmanTree; public class HNode { /** * 节点类 * @author LiRui * */ public String code = "";// 节点的哈夫曼编码 public String data = "";// 节点的数据 public int count;// 节点的权值 public HNode lChild; public HNode rChild; public HNode() { } public HNode(String data, int count) { this.data = data; this.count = count; } public HNode(int count, HNode lChild, HNode rChild) { this.count = count; this.lChild = lChild; this.rChild = rChild; } public HNode(String data, int count, HNode lChild, HNode rChild) { this.data = data; this.count = count; this.lChild = lChild; this.rChild = rChild; } }
public HuffmanNode<T> createTree(List<HuffmanNode<T>> nodes) { while (nodes.size() > 1) { Collections.sort(nodes); HuffmanNode<T> left = nodes.get(nodes.size() - 2);//令其左孩子的编码为0 left.setCode("0"); HuffmanNode<T> right = nodes.get(nodes.size() - 1);//令其右孩子的编码为1 right.setCode("1"); HuffmanNode<T> parent = new HuffmanNode<T>(null, left.getWeight() + right.getWeight()); parent.setlChild(left); parent.setrChild(right); nodes.remove(left); nodes.remove(right); nodes.add(parent); } return nodes.get(0); }
这个是构建哈夫曼树的具体流程,由于须要在结点已经链接以后在进行比较因此就将全部出现的字符放在一个链表里便于操做。具体的流程代码有详细介绍。git
/** * 将出现的字符建立成单个的结点对象 */ private void creatNodes() { for (int i = 0; i < charList.size(); i++) { String data = charList.get(i).c + ""; int count = charList.get(i).num; HNode node = new HNode(data, count); // 建立节点对象 NodeList.add(node); // 加入到节点链表 } } /** * 构建哈夫曼树 */ private void creatTree() { while (NodeList.size() > 1) {// 当节点数目大于一时 // 4.取出权值最小的两个节点,生成一个新的父节点 // 5.删除权值最小的两个节点,将父节点存放到列表中 HNode left = NodeList.poll(); HNode right = NodeList.poll(); // 在构建哈夫曼树时设置各个结点的哈夫曼编码 left.code = "0"; right.code = "1"; setCode(left); setCode(right); int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和 HNode parent = new HNode(parentWeight, left, right); NodeList.addFirst(parent); // 将父节点置于首位 Sort(NodeList); // 从新排序,避免新节点权值大于链表首个结点的权值 } }
这个是对结点链表进行排序的类算法
/** * 升序排序 * * @param nodelist */ private void Sort(LinkedList<HNode> nodelist) { for (int i = 0; i < nodelist.size() - 1; i++) { for (int j = i + 1; j < nodelist.size(); j++) { HNode temp; if (nodelist.get(i).count > nodelist.get(j).count) { temp = nodelist.get(i); nodelist.set(i, nodelist.get(j)); nodelist.set(j, temp); } } } }
下面介绍一个类这个主要的做用是统计各个字符出现的次数,也就是老师所说的几率,而后再进行排序。测试
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); if(ch == data.c){ // 字符对象链表中有相同字符则将个数加1 data.num++; flag = false; break; } } if(flag){ // 字符对象链表中没有相同字符则建立新对象加如链表 charList.add(new CharData(ch)); } } }
这是输出每一个结点编码的类ui
private void output(HNode node) { if (node.lChild == null && node.rChild == null) { System.out.println(node.data + ": " + node.code); } if (node.lChild != null) { output(node.lChild); } if (node.rChild != null) { output(node.rChild); } } /** * 输出结果字符的哈夫曼编码 */ public void output() { output(root); }
这是编码的过程,主要原理是经过经过一个查找的方法不断地将传进的字符串在哈夫曼树中找到并返回他的哈夫曼编码最后在输出出来。this
/** * 编码 * @param str * @return */ public String toHufmCode(String str) { for (int i = 0; i < str.length(); i++) { String c = str.charAt(i) + ""; search(root, c); } return hfmCodeStr; } /** * * @param root 哈夫曼树根节点 * @param c 须要生成编码的字符 */ private void search(HNode 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); } }
解码部分与之相似。编码
- // 保存解码的字符串 String result=""; boolean target = false; // 解码标记 /** * 解码 * @param codeStr * @return */ public String CodeToString(String codeStr) { int start = 0; int end = 1; while(end <= codeStr.length()){ target = false; String s = codeStr.substring(start, end); matchCode(root, s); // 解码 // 每解码一个字符,start向后移 if(target){ start = end; } end++; } return result; } /** * 匹配字符哈夫曼编码,找到对应的字符 * @param root 哈夫曼树根节点 * @param code 须要解码的二进制字符串 */ private void matchCode(HNode 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); } }
至于我上传的测试类中的统计几率的方法是借鉴的李馨雨同窗的。
文件读写就太简单了相信学长学姐们都懂。.net
哈夫曼是一种用于压缩的算法,是一种很实用的算法,但多是我的能力的限制,在具体实现过程当中碰见了不少困难,在具体的代码实现中,借鉴了不少网页和同窗的思路。