20172303 2018-2019-1《程式設計與資料結構》哈夫曼樹編碼與解碼
阿新 • • 發佈:2018-12-12
20172303 2018-2019-1《程式設計與資料結構》哈夫曼樹編碼與解碼
哈夫曼樹簡介
- 定義:給定n個權值作為n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。
- 帶權路徑長度(Weighted Path Length of Tree,簡記為WPL)
- 結點的權:在一些應用中,賦予樹中結點的一個有某種意義的實數。
- 結點的帶權路徑長度:結點到樹根之間的路徑長度與該結點上權的乘積。
- 樹的帶權路徑長度(Weighted Path Length of Tree):定義為樹中所有葉結點的帶權路徑長度之和。
哈夫曼樹程式碼實現
- 程式碼參考了java建立哈夫曼樹和實現哈夫曼編碼
HuffmanNode類
- 首先設定一個
HuffmanNode
類作為實現的基礎,每個結點都包含一個六項內容:權值、結點代表字母、字母的編碼、左孩子、右孩子和父結點,為了方便之後進行結點的比較,這裡還重新編寫了一下compareTo
方法。
public int compareTo(HuffmanNode<T> o) { if (this.getWeight() > o.getWeight()){ return -1; } else if (this.getWeight() < o.getWeight()){ return 1; } return 0; }
HuffmanTree類
- 在
HuffmanTree
類裡有兩個方法,第一個方法createTree
方法用於構造樹,第二個方法BFS
方法是使用廣度優先遍歷來給每一個葉子結點進行編碼。具體方法及步驟在程式碼中都已寫明。
public static HuffmanNode createTree(List<HuffmanNode<String>> nodes) { while (nodes.size() > 1){ // 對陣列進行排序 Collections.sort(nodes); // 當列表中還有兩個以上結點時,構造樹 // 獲取權值最小的兩個結點 HuffmanNode left = nodes.get(nodes.size() - 2); left.setCode(0 + ""); HuffmanNode right = nodes.get(nodes.size() - 1); right.setCode(1 + ""); // 生成新的結點,新結點的權值為兩個子節點的權值之和 HuffmanNode parent = new HuffmanNode(left.getWeight() + right.getWeight(), null); // 使新結點成為父結點 parent.setLeft(left); parent.setRight(right); // 刪除權值最小的兩個結點 nodes.remove(left); nodes.remove(right); nodes.add(parent); } return nodes.get(0); } public static List<HuffmanNode> BFS(HuffmanNode root){ Queue<HuffmanNode> queue = new ArrayDeque<HuffmanNode>(); List<HuffmanNode> list = new java.util.ArrayList<HuffmanNode>(); if (root != null){ // 將根元素加入佇列 queue.offer(root); root.getLeft().setCode(root.getCode() + "0"); root.getRight().setCode(root.getCode() + "1"); } while (!queue.isEmpty()){ // 將佇列的隊尾元素加入列表中 list.add(queue.peek()); HuffmanNode node = queue.poll(); // 如果左子樹不為空,將它加入佇列並編碼 if (node.getLeft() != null){ queue.offer(node.getLeft()); node.getLeft().setCode(node.getCode() + "0"); } // 如果右子樹不為空,將它加入佇列並編碼 if (node.getRight() != null){ queue.offer(node.getRight()); node.getRight().setCode(node.getCode() + "1"); } } return list; }
HuffmanMakeCode類
HuffmanMakeCode
類用於將檔案中的內容提取,放入陣列並進行計數,這裡將陣列長度設定為27,因為還對空格進行了計數,以便於解碼。具體方法及步驟在程式碼中都已寫明。
public class HuffmanMakeCode {
public static char[] word = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s'
,'t','u','v','w','x','y','z',' '};
public static int[] number = new int[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
public static String makecode(FileInputStream stream) throws IOException {
//讀取檔案(快取位元組流)
BufferedInputStream in = new BufferedInputStream(stream);
//一次性取多少位元組
byte[] bytes = new byte[2048];
//接受讀取的內容(n就代表的相關資料,只不過是數字的形式)
int n = -1;
String a = null;
//迴圈取出資料
while ((n = in.read(bytes, 0, bytes.length)) != -1) {
//轉換成字串
a = new String(bytes, 0, n, "GBK");
}
// 對檔案內容進行計數
count(a);
return a;
}
// 實現對檔案內容計數,內層迴圈依次比較字串中的每個字元與對應字元是否相同,相同時計數;外層迴圈指定對應字元從a至空格
public static void count(String str){
for (int i = 0;i < 27;i++){
int num = 0;
for (int j = 0;j < str.length();j++){
if (str.charAt(j) == word[i]){
num++;
}
}
number[i] += num;
}
}
public static char[] getWord() {
return word;
}
public static int[] getNumber() {
return number;
}
}
HuffmanTest類
HuffmanTest
類進行了檔案的讀取,構造哈夫曼樹,編碼,解碼,檔案的寫入五個步驟,其中前三個步驟使用之前三個類中的方法即可實現,這裡主要說一下後兩個步驟。- 解碼:解碼部分使用一個列表list4將編碼結果的字串轉化到列表中去,然後定義了兩個變數,第一個變數用於每次依次獲取的編碼值,然後與list3(儲存編碼的列表)進行比較找到對應索引,然後將list2(儲存字母的列表)中對應索引值位置的字母加入第二個變數中,每次迴圈後刪除列表list4的第一個元素,迴圈直至list4為空時結束,第二個變數temp1中儲存的即為解碼結果。
- 檔案寫入:檔案寫入就是很簡單的方法使用,這裡使用的是字元操作流(使用FileWriter類和FileReader類)的方法。
// 進行解碼
List<String> list4 = new ArrayList<>();
for (int i = 0;i < result.length();i++){
list4.add(result.charAt(i) + "");
}
String temp = "";
String temp1 = "";
while (list4.size() > 0){
temp += "" + list4.get(0);
list4.remove(0);
for (int i = 0;i < list3.size();i++){
if (temp.equals(list3.get(i))){
temp1 += "" + list2.get(i);
temp = "";
}
}
}
System.out.println("檔案解碼結果為: " + temp1);
// 寫入檔案
File file = new File("C:\\Users\\45366\\IdeaProjects\\fwq20172303_Programming\\HuffmanTest2.txt");
Writer out = new FileWriter(file);
out.write(result);
out.close();