package com.eleven.binaryTree.huffman.code;
import java.util.*;
/**
* @Description 用赫夫曼編碼進行壓縮和解碼
* @auther Eleven
* @create 2020-04-07 23:12
**/
public class HuffManCodeCus {
public static void main(String[] args) {
HuffManCodeCus huffManCodeCus = new HuffManCodeCus();
String msg = " can you think about it . ";
huffManCodeCus.huffManCodeCus(msg);
}
//創建赫夫曼編碼
public void huffManCodeCus(String str){
byte[] bytes = str.getBytes();
System.out.println("壓縮前:"+bytes.length);
System.out.println(Arrays.toString(bytes));
//將byte數字轉化爲赫夫曼樹的node(這裏要注意所有權重的計算,是以每個數出現的次數)
List<Node> nodes = createNodeList(bytes);
//將nodes轉化爲赫夫曼樹
Node tree = createTree(nodes);
//創建赫夫曼編碼表(所謂的編碼表就是指從根節點到葉子節點的路徑 左邊爲0右邊用1標示)
Map<Byte,String> huffManCodeTable = createCode(tree);
// System.out.println(huffManCodeTable);
//根據編碼表進行壓縮
byte[] zipBytes = zip(huffManCodeTable,bytes);
//System.out.println("壓縮前:"+zipBytes.length);
//解碼
byte[] unZipBytes = unZip(huffManCodeTable,zipBytes);
System.out.println(new String(unZipBytes));
}
//解碼
private byte[] unZip(Map<Byte, String> huffManCodeTable, byte[] zipBytes) {
Map<String, Byte> map = new HashMap<>();
StringBuilder sb = new StringBuilder();
//首先將編碼表的鍵值對互換位置
for (Map.Entry<Byte, String> entry:huffManCodeTable.entrySet()){
map.put(entry.getValue(),entry.getKey());
}
//轉化爲8位二進制數據
for(int i = 0;i<zipBytes.length;i++){
byte bty = zipBytes[i];
boolean flag=(i==zipBytes.length-1);
String str = byteToString(!flag,bty);
sb.append(str);
}
System.out.println(sb.toString());
//將數據轉化爲byte
int count =0;
List<Byte> list = new ArrayList<Byte>();
for (int i = 0;i<sb.length();i++){
String b = sb.substring(count,i);
if (map.get(b) !=null){
list.add(map.get(b));
count=i;
}
}
//轉成數組
byte[] bytes = new byte[list.size()];
for (int m=0;m<list.size();m++){
bytes[m] = list.get(m);
}
return bytes;
}
/**
* 轉成8位二進制數據
* @param flag 判斷是否爲最後一位
* @param b 數組
* @return
*/
private String byteToString(boolean flag ,byte b) {
int by = b;
if (flag){
by|=256;
}
String temp = Integer.toBinaryString(by);
if (flag){
temp = temp.substring(temp.length()-8);
}else{
temp = temp;
}
return temp;
}
private byte[] zip(Map<Byte, String> huffManCodeTable, byte[] bytes) {
//轉化爲二進制編碼
StringBuilder zipSb = new StringBuilder();
for (int i=0;i<bytes.length;i++){
zipSb.append(huffManCodeTable.get(bytes[i]));
}
System.out.println(zipSb.toString());
//再變成壓縮後的byte數組
//使用8爲二進制數標示所以存儲壓縮後byte的長度計算方式如下
int len;
if (zipSb.length()%8 ==0){
len = zipSb.length()/8;
}else {
len = zipSb.length()/8+1;
}
byte[] zipResbytes = new byte[len];
int index = 0;
//每8位一截取
for (int i=0;i<zipSb.length();i=i+8){
String temp = "";
if (zipSb.length()-i<8){
temp = zipSb.substring(i);
}else {
temp = zipSb.substring(i,i+8);
}
//轉換爲二進制
byte b = (byte) Integer.parseInt(temp,2);
zipResbytes[index] = b;
index++;
}
return zipResbytes;
}
static StringBuilder sb = new StringBuilder();
static Map<Byte, String> huffManMap = new HashMap<>();
private Map<Byte, String> createCode(Node tree) {
if (tree == null){
return null;
}
//創建路徑編碼
createCodes(tree.left,"0",sb);
createCodes(tree.right,"1",sb);
return huffManMap;
}
private void createCodes(Node node, String s, StringBuilder sb) {
StringBuilder stringBuilder = new StringBuilder(sb);
stringBuilder.append(s);
if (node.data==null){
createCodes(node.left,"0",stringBuilder);
createCodes(node.right,"1",stringBuilder);
}else {
huffManMap.put(node.data,stringBuilder.toString());
}
}
/**
* 創建赫夫曼樹
* @param nodes
* @return
*/
private Node createTree(List<Node> nodes) {
//首先要對集合進行排序
while (nodes.size()>1) {
Collections.sort(nodes);
//取出最小的左右的節點
Node left = nodes.get(nodes.size() - 1);
Node right = nodes.get(nodes.size() - 2);
//新建一個父節點 並且設置好左右節點
Node parent = new Node(null, left.weight + right.weight);
parent.left = left;
parent.right = right;
//將左右節點移除
nodes.remove(left);
nodes.remove(right);
//將父節點添加進去
nodes.add(parent);
//再接着排序循環以上步驟
}
//返回根節點
return nodes.get(0);
}
/**
* 轉化爲赫夫曼樹的葉子節點
* @param bytes
* @return
*/
private List<Node> createNodeList(byte[] bytes) {
List<Node> nodes = new ArrayList<>();
//計算數組中每個數出現的次數,作爲權重
Map<Byte,Integer> map = new HashMap<>();
for (int i=0;i<bytes.length;i++){
if (map.get(bytes[i])==null){
map.put(bytes[i],1);
}else {
int count = map.get(bytes[i]);
map.put(bytes[i],count+1);
}
}
//根據權重和數據創建葉子節點集合
for (Map.Entry<Byte,Integer> entry:map.entrySet()){
Node node = new Node(entry.getKey(),entry.getValue());
nodes.add(node);
}
return nodes;
}
}
赫夫曼編碼進行壓縮和解碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.