這個老師上課說過。首先肯定文件中各個字符的機率而後將其。按照機率大小。將機率小的放在離根節點較近的地方。
這裏用數字來表明字符的機率而後,首先將機率最小的兩個字符放到一塊兒,將它們的機率合成父結點的機率,而後父結點參與比較再次在備選集中找到比較小的兩兩結合,而後再次合成父結點的機率。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
哈夫曼是一種用於壓縮的算法,是一種很實用的算法,但多是我的能力的限制,在具體實現過程當中碰見了不少困難,在具體的代碼實現中,借鑑了不少網頁和同窗的思路。