解碼:譯碼過程是分解、識別各個字符,還原數據的過程。從字符串頭開始掃描到尾,依次去匹配。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