既然是學習哈夫曼編碼,咱們首先須要知道什麼是哈夫曼樹:給定n個權值做爲n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹爲最優二叉樹,也稱爲哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。
java
在平常計算機的使用中,咱們必定會出現下面這種狀況:假如給定a、b、c、d、e五個字符,它們在文本中出現的機率以下圖所示:node
字符 | 機率 |
---|---|
a | 0.12 |
b | 0.40 |
c | 0.15 |
d | 0.05 |
e | 0.25 |
咱們如今要將文本編碼成0/1序列從而使得計算機可以進行讀取和計算。爲了保證每一個字符的獨一性,因此咱們給予不一樣的的字符以不一樣的編碼。若是給每一個字符賦予等長的編碼的話,會使得平均的編碼長度過長,影響計算時的性能,浪費計算機的資源(定長編碼的缺點)。這時咱們就想到了變長編碼,理所固然的,給出現機率較大的字符賦予較短的編碼,機率較小的字符賦予較長的編碼,這樣在計算的時候不就能夠節省不少時間了嗎?可這樣咱們又面臨到了一個巨大的問題,咱們來看下面這種狀況,咱們對字符進行編碼:git
字符 | 機率 | 編碼 |
---|---|---|
a | 0.12 | 01 |
b | 0.40 | 0 |
c | 0.15 | 00 |
d | 0.05 | 10 |
e | 0.25 | 1 |
假設如今文本中的字符是bcd,轉換以後的0/1序列爲00010,可咱們要在轉換成文本的時候到底是把第一位的0讀做b仍是把前兩位的00讀做c呢?爲了解決這個問題,就又有了前綴碼的概念。顧名思義,前綴碼的含義就是任意字符的編碼都不是其餘字符編碼的前綴。那麼該如何造成前綴碼呢?首先咱們要構造一棵二叉樹,指向左孩子的"邊"記做0,指向右孩子的點記做「1」,葉子節點爲代編碼的字符,出現機率越大的字符離根的距離就越近。
算法
字符 | 機率 | 編碼 |
---|---|---|
a | 0.12 | 0100 |
b | 0.40 | 1 |
c | 0.15 | 0101 |
d | 0.05 | 011 |
e | 0.25 | 00 |
咱們在前面提到:哈夫曼樹的帶權路徑最小,因此有哈夫曼樹構成的前綴碼記做哈夫曼編碼。哈夫曼做爲已知的最佳無損壓縮算法,知足前綴碼的性質,能夠即時解碼。數組
哈夫曼編碼(Huffman Coding),又稱霍夫曼編碼,是一種編碼方式,哈夫曼編碼是可變字長編碼(VLC)的一種。Huffman於1952年提出一種編碼方法,該方法徹底依據字符出現機率來構造異字頭的平均長度最短的碼字,有時稱之爲最佳編碼,通常就叫作Huffman編碼(有時也稱爲霍夫曼編碼)。app
實現哈夫曼編碼的主要思路爲從指定的文件中讀出文本,首先經過遍歷得到各個字符出現的機率,根據出現機率的大小構造二叉樹,在此基礎上進行編碼解碼。ide
public class Node<T> implements Comparable<Node<T>> { private T data; private double weight; private Node<T> left; private Node<T> right; String code; public Node(T data, double weight){ this.data = data; this.weight = weight; this.code = ""; } public T getData() { return data; } public void setData(T data) { this.data = data; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public Node<T> getLeft() { return left; } public void setLeft(Node<T> left) { this.left = left; } public Node<T> getRight() { return right; } public void setRight(Node<T> right) { this.right = right; } public String getCode(){ return code; } public void setCode(String str){ code = str; } @Override public String toString(){ return "data:"+this.data+";weight:"+this.weight+";code: "+this.code; } @Override public int compareTo(Node<T> other) { if(other.getWeight() > this.getWeight()){ return 1; } if(other.getWeight() < this.getWeight()){ return -1; } return 0; } }
public Node<T> createTree(List<Node<T>> nodes) { while (nodes.size() > 1) { Collections.sort(nodes); Node<T> left = nodes.get(nodes.size() - 2); left.setCode(0 + ""); Node<T> right = nodes.get(nodes.size() - 1); right.setCode(1 + ""); Node<T> parent = new Node<T>(null, left.getWeight() + right.getWeight()); parent.setLeft(left); parent.setRight(right); nodes.remove(left); nodes.remove(right); nodes.add(parent); } return nodes.get(0); }
public List<Node<T>> breadth(Node<T> root) { List<Node<T>> list = new ArrayList<Node<T>>(); Queue<Node<T>> queue = new ArrayDeque<Node<T>>(); 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()); Node<T> node = queue.poll(); if (node.getLeft() != null) node.getLeft().setCode(node.getCode() + "0"); if (node.getRight() != null) node.getRight().setCode(node.getCode() + "1"); if (node.getLeft() != null) { queue.offer(node.getLeft()); } if (node.getRight() != null) { queue.offer(node.getRight()); } } return list; }
File file = new File("G:/usually/input/input1.txt");
public class readtxt { char[] chars = 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',' '}; 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 String txtString(File file){ StringBuilder result = new StringBuilder(); try{ BufferedReader br = new BufferedReader(new FileReader(file));//構造一個BufferedReader類來讀取文件 String s = null; while((s = br.readLine())!=null){//使用readLine方法,一次讀一行 result.append(System.lineSeparator()+s); num(s); } br.close(); }catch(Exception e){ e.printStackTrace(); } return result.toString(); } public void num(String string){ for(int i = 0;i<27;i++){ int temp = 0; for(int j = 0;j<string.length();j++){ if(string.charAt(j) == chars[i]) temp++; } number[i] += temp; } } public int[] getNumber(){ return number; } public char[] getChars(){ return chars; } }
readtxt read = new readtxt(); String temp = read.txtString(file); int[] num = read.getNumber(); char[] chars = read.getChars();
for(int i = 0;i<27;i++){ System.out.print(chars[i]+":"+num[i]+" "); list.add(new Node<String>(chars[i]+"",num[i])); }
HuffmanTree huffmanTree = new HuffmanTree(); Node<String> root = huffmanTree.createTree(list);
list2=huffmanTree.breadth(root); for(int i = 0;i<list2.size();i++){ if(list2.get(i).getData()!=null) { list3.add(list2.get(i).getData()); list4.add(list2.get(i).getCode()); } }
for(int i = 0;i<temp.length();i++){ for(int j = 0;j<list3.size();j++){ if(temp.charAt(i) == list3.get(j).charAt(0)) result += list4.get(j); } }
for(int i = 0;i<result.length();i++){ list5.add(result.charAt(i)+""); } while (list5.size()>0){ temp2 = temp2+"" +list5.get(0); list5.remove(0); for(int i=0;i<list4.size();i++){ if (temp2.equals(list4.get(i))) { temp3 = temp3+""+list3.get(i); temp2 = ""; } } }
File file2 =new File("G:/usually/input/input2.txt"); Writer out =new FileWriter(file2); out.write(temp3); out.close();
最後獲得的結果:性能