目 錄
P40-4.13 赫夫曼樹概述
最優二叉樹(赫夫曼樹):它是n個帶權葉子結點構成的所有二叉樹中,帶權路徑長度最小的二叉樹。
P41-4.14 創建赫夫曼樹的流程分析
赫夫曼樹
P42-4.15 代碼實現創建赫夫曼樹
Alt + Shift + S【快捷鍵】
1、Node.java
package demo9;
public class Node implements Comparable<Node> {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public int compareTo(Node o) {
return -(this.value - o.value);// 加“-”,大在前
}
// Alt + Shift + S【快捷鍵】
@Override
public String toString() {
return "Node [value=" + value + "]";
}
}
2、TestHuffmanTree.java
package demo9;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestHuffmanTree {
public static void main(String[] args) {
int[] arr = { 3, 7, 8, 29, 5, 11, 23, 14 };
Node node = createHuffmanTree(arr);
System.out.println(node);
}
// 創建赫夫曼樹
public static Node createHuffmanTree(int[] arr) {
// 1、先使用數組中所有的元素創建若干個二叉樹,(只有一個節點)
List<Node> nodes = new ArrayList<>();// 創建數組集合,用來存二叉樹
for (int value : arr) { // for循環的另外一種表達,遍歷數組元素用的
nodes.add(new Node(value));
}
// Collections.sort(nodes);
// System.out.println(nodes);
// 循環處理!!!
while (nodes.size() > 1) { // 只有1顆樹時, 結束處理!
// 2、排序【排序前提:Node可以排序!】
Collections.sort(nodes);// 需要Node實現接口
// 3、取出來權值最小的兩個二叉樹
// 3.1、取出最權值最小的二叉樹
Node left = nodes.get(nodes.size() - 1);
// 3.2、取出最權值次小的二叉樹
Node right = nodes.get(nodes.size() - 2);
// 4、創建一顆新的二叉樹
Node parent = new Node(left.value + right.value);
// 5、把取出來的兩個二叉樹移除
nodes.remove(left);
nodes.remove(right);
// 6、放入原來的二叉樹集合中
nodes.add(parent);
}
return nodes.get(0);
}
}
P43-4.16 赫夫曼編碼原理分析
1、通信領域中信息的處理1-定長編碼
赫夫曼編碼的運用
計算機並不能直接將字符發給別人。
先將 字符 按照某個標準(ASCII)轉換爲數字。【c-->99,a-->97】
【計算機只能識別0、1串,每一個數字都是一個byte(字節)】
再將 數字 轉換爲 8位的字節,
【1100011--->99--->c】
將01串發給另一臺計算機B,計算機B按 8位 劃分 01串,
將 01串 轉換爲 十進制數字,再將 十進制數字 根據 ASCII 轉換爲 字母。
定長編碼方式,傳輸效率 極低!!!
固定長度~浪費空間 ==> 非定長編碼
2、通信領域中信息的處理2-非定長編碼
先對計算串進行處理,統計字符串中每個字符出現了多少次,
令經常出現的字符長度長一些,不經常出現的字符長度短一些,【不按照ASCII進行處理】
將次數當作權值構造哈夫曼!!!
3、通信領域中信息的處理3-赫夫曼編碼
字符出現次數少的,在樹底;
字符出現次數多的,在樹頂。
左0 右1
將 字符串 按照 赫夫曼編碼表 進行編碼,
按照 編碼表 進行 解碼,無損壓縮!
P44-4.17 數據壓縮之創建赫夫曼樹
1、Map從入門到性能分析
Java---Map從入門到性能分析
https://blog.csdn.net/weixin_44949135/article/details/106862811
2、思路分析
1、統計字符出現次數,並排序;
2、根據排序結果,將字符轉換爲節點;
3、將節點轉換爲赫夫曼樹;
字符 可以轉換爲 byte。
// 節點 要存儲 字符(data) 與 權值(weight-字符出現的次數)
// 每個字符作爲一個節點
// 按照 權值(weight-字符出現的次數) 進行 排序
Byte data;// 存儲節點代表的字符/英文字符可轉byte
int weight;// 權值
Node left;// 左節點
Node right;// 右節點
// 創建赫夫曼樹的時候,新創建的節點,無data,只有weight data可爲空
不 對 字符串 進行 編碼,對 字符串的byte數組 進行 編碼。
所有的數據 都 可以 轉換爲 byte數組。【將 數據 轉換爲 byte數組--->文件操作!!!】
3、代碼實現
3.1、Node.java
package demo10;
public class Node implements Comparable<Node> {
// 節點 要存儲 字符(data) 與 權值(weight-字符出現的次數)
// 每個字符作爲一個節點
// 按照 權值(weight-字符出現的次數) 進行 排序
Byte data;// 存儲節點代表的字符/英文字符可轉byte
int weight;// 權值
Node left;// 左節點
Node right;// 右節點
// 創建赫夫曼樹的時候,新創建的節點,無data,只有weight|data可爲空
public Node(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override
public String toString() {
return "Node [data=" + data + ", weight=" + weight + "]";
}
@Override
public int compareTo(Node o) {
return o.weight - this.weight;//倒序
}
}
3.2、TestHuffmanCode44.java
package demo10;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestHuffmanCode44 {
public static void main(String[] args) {
String msg = "can you can a can as a can canner can a can.";
/**
* 赫夫曼編碼 不直接對字符串進行編碼。而是編碼字符串的byte數組。
* 所有的數據都可以轉換爲byte數組。文件壓縮使用byte數組。
* 文件讀取出來是一個byte數組。所以,將數據轉換爲byte數組。
*/
byte[] bytes = msg.getBytes();
byte[] b = huffmanZip(bytes);// 獲取使用赫夫曼編碼壓縮後的byte數組
}
/**
* 進行赫夫曼編碼壓縮的方法
*
* @param bytes
* @return
*/
private static byte[] huffmanZip(byte[] bytes) {
// 1、先統計每一個byte出現的次數,並放入一個集合中【getNodes()】
List<Node> nodes = getNodes(bytes);
// 2、創建一棵赫夫曼樹
Node tree = createHuffmanTree(nodes);
System.out.println(tree);
System.out.println(tree.right);
System.out.println(tree.left);
// 3、創建一個赫夫曼編碼表
// 4、編碼
return null;
}
/**
* 1、把byte數組轉爲node集合
*
* @param bytes
* @return
*/
private static List<Node> getNodes(byte[] bytes) {
List<Node> nodes = new ArrayList<>();
// 存儲每一個byte出現了多少次。
Map<Byte, Integer> counts = new HashMap<>();
// 統計每一個byte出現的次數
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
//System.out.println(counts);
//{32=11, 97=11, 114=1, 99=7, 115=1, 117=1, 101=1, 121=1, 110=8, 46=1, 111=1}
// 32-空格、97-a
// 把每一個鍵值對轉爲一個node對象
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
/**
* 2、創建赫夫曼樹
*
* @param nodes
* @return
*/
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
// 排序
Collections.sort(nodes);
// System.out.println(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);
}
}
P45-4.18 數據壓縮之創建編碼表&編碼
1、TestHuffmanCode45.java
package demo10;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestHuffmanCode45 {
public static void main(String[] args) {
String msg = "can you can a can as a can canner can a can.";
/**
* 赫夫曼編碼 不直接對字符串進行編碼。而是編碼字符串的byte數組。
* 所有的數據都可以轉換爲byte數組。文件壓縮使用byte數組。
* 文件讀取出來是一個byte數組。所以,將數據轉換爲byte數組。
*/
byte[] bytes = msg.getBytes();
byte[] b = huffmanZip(bytes);// 獲取使用赫夫曼編碼壓縮後的byte數組
System.out.println(bytes.length);
System.out.println(b.length);
}
/**
* 進行赫夫曼編碼壓縮的方法
*
* @param bytes
* @return
*/
private static byte[] huffmanZip(byte[] bytes) {
// 1、先統計每一個byte出現的次數,並放入一個集合中【getNodes()】
List<Node> nodes = getNodes(bytes);
// 2、創建一棵赫夫曼樹
Node tree = createHuffmanTree(nodes);
System.out.println(tree);
System.out.println(tree.right);
System.out.println(tree.left);
// 3、創建一個赫夫曼編碼表
Map<Byte, String> huffCodes = getCodes(tree);
System.out.println(huffCodes);
// 4、編碼
byte[] b = zip(bytes, huffCodes);
return b;
}
/**
* 1、把byte數組轉爲node集合
*
* @param bytes
* @return
*/
private static List<Node> getNodes(byte[] bytes) {
List<Node> nodes = new ArrayList<>();
// 存儲每一個byte出現了多少次。
Map<Byte, Integer> counts = new HashMap<>();
// 統計每一個byte出現的次數
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
//System.out.println(counts);
//{32=11, 97=11, 114=1, 99=7, 115=1, 117=1, 101=1, 121=1, 110=8, 46=1, 111=1}
// 32-空格、97-a
// 把每一個鍵值對轉爲一個node對象
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
/**
* 2、創建赫夫曼樹
*
* @param nodes
* @return
*/
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
// 排序
Collections.sort(nodes);
// System.out.println(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);
}
// 用於臨時存儲路徑
static StringBuilder sb = new StringBuilder();
// 用於存儲赫夫曼編碼
static Map<Byte, String> huffCodes = new HashMap<>();
/**
* 3、根據赫夫曼樹獲取赫夫曼編碼
* 需要保存前面樹支的結果0、1
* @param tree
* @return
*/
private static Map<Byte, String> getCodes(Node tree) {
if (tree == null) {
return null;
}
getCodes(tree.left, "0", sb);
getCodes(tree.right, "1", sb);
return huffCodes;
}
private static void getCodes(Node node, String code, StringBuilder sb) {
StringBuilder sb2 = new StringBuilder(sb);
sb2.append(code);
if (node.data == null) {
getCodes(node.left, "0", sb2);
getCodes(node.right, "1", sb2);
} else {
huffCodes.put(node.data, sb2.toString());
}
}
/**
* 4、進行赫夫曼編碼
*
* @param bytes
* @param huffCodes2
* @return
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffCodes) {
StringBuilder sb = new StringBuilder();
// 把需要壓縮的byte數組處理成一個二進制的字符串
for (byte b : bytes) {
sb.append(huffCodes.get(b));
}
// System.out.println(sb.toString());
// 定義長度
int len;
if (sb.length() % 8 == 0) {
len = sb.length() / 8;
} else {
len = sb.length() / 8 + 1;
}
// 用於存儲壓縮後的byte
byte[] by = new byte[len];
// 記錄新byte的位置
int index = 0;
for (int i = 0; i < sb.length(); i += 8) {
String strByte;
if (i + 8 > sb.length()) {
strByte = sb.substring(i);
} else {
strByte = sb.substring(i, i + 8);
}
byte byt = (byte) Integer.parseInt(strByte, 2);//二進制轉十進制
System.out.println(strByte + " : " + byt);
by[index] = byt;
index++;
}
return by;
}
}
P46-4.19 使用赫夫曼編碼進行解碼
package demo10;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestHuffmanCode45 {
public static void main(String[] args) {
String msg = "can you can a can as a can canner can a can.";
/**
* 赫夫曼編碼 不直接對字符串進行編碼。而是編碼字符串的byte數組。
* 所有的數據都可以轉換爲byte數組。文件壓縮使用byte數組。
* 文件讀取出來是一個byte數組。所以,將數據轉換爲byte數組。
*/
byte[] bytes = msg.getBytes();
//進行赫夫曼編碼壓縮
byte[] b = huffmanZip(bytes);// 獲取使用赫夫曼編碼壓縮後的byte數組
System.out.println(bytes.length);
System.out.println(b.length);
//使用赫夫曼編碼進行解碼
byte[] newBytes = decode(huffCodes,b);
System.out.println(new String(newBytes));
}
/**
* 進行赫夫曼編碼壓縮的方法
*
* @param bytes
* @return
*/
private static byte[] huffmanZip(byte[] bytes) {
// 1、先統計每一個byte出現的次數,並放入一個集合中【getNodes()】
List<Node> nodes = getNodes(bytes);
// 2、創建一棵赫夫曼樹
Node tree = createHuffmanTree(nodes);
System.out.println(tree);
System.out.println(tree.right);
System.out.println(tree.left);
// 3、創建一個赫夫曼編碼表
Map<Byte, String> huffCodes = getCodes(tree);
System.out.println(huffCodes);
// 4、編碼
byte[] b = zip(bytes, huffCodes);
return b;
}
/**
* 1、把byte數組轉爲node集合
*
* @param bytes
* @return
*/
private static List<Node> getNodes(byte[] bytes) {
List<Node> nodes = new ArrayList<>();
// 存儲每一個byte出現了多少次。
Map<Byte, Integer> counts = new HashMap<>();
// 統計每一個byte出現的次數
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
//System.out.println(counts);
//{32=11, 97=11, 114=1, 99=7, 115=1, 117=1, 101=1, 121=1, 110=8, 46=1, 111=1}
// 32-空格、97-a
// 把每一個鍵值對轉爲一個node對象
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
/**
* 2、創建赫夫曼樹
*
* @param nodes
* @return
*/
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
// 排序
Collections.sort(nodes);
// System.out.println(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);
}
// 用於臨時存儲路徑
static StringBuilder sb = new StringBuilder();
// 用於存儲赫夫曼編碼
static Map<Byte, String> huffCodes = new HashMap<>();
/**
* 3、根據赫夫曼樹獲取赫夫曼編碼
* 需要保存前面樹支的結果0、1
* @param tree
* @return
*/
private static Map<Byte, String> getCodes(Node tree) {
if (tree == null) {
return null;
}
getCodes(tree.left, "0", sb);
getCodes(tree.right, "1", sb);
return huffCodes;
}
private static void getCodes(Node node, String code, StringBuilder sb) {
StringBuilder sb2 = new StringBuilder(sb);
sb2.append(code);
if (node.data == null) {
getCodes(node.left, "0", sb2);
getCodes(node.right, "1", sb2);
} else {
huffCodes.put(node.data, sb2.toString());
}
}
/**
* 4、進行赫夫曼編碼
*
* @param bytes
* @param huffCodes2
* @return
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffCodes) {
StringBuilder sb = new StringBuilder();
// 把需要壓縮的byte數組處理成一個二進制的字符串
for (byte b : bytes) {
sb.append(huffCodes.get(b));
}
// System.out.println(sb.toString());
// 定義長度
int len;
if (sb.length() % 8 == 0) {
len = sb.length() / 8;
} else {
len = sb.length() / 8 + 1;
}
// 用於存儲壓縮後的byte
byte[] by = new byte[len];
// 記錄新byte的位置
int index = 0;
for (int i = 0; i < sb.length(); i += 8) {
String strByte;
if (i + 8 > sb.length()) {
strByte = sb.substring(i);
} else {
strByte = sb.substring(i, i + 8);
}
byte byt = (byte) Integer.parseInt(strByte, 2);//二進制轉十進制
System.out.println(strByte + " : " + byt);
by[index] = byt;
index++;
}
return by;
}
/**
* 使用指定的赫夫曼編碼表進行解碼
*
* @param huffCodes2
* @param b
* @return
*/
private static byte[] decode(Map<Byte, String> huffCodes, byte[] bytes) {
StringBuilder sb = new StringBuilder();
// 把byte數組轉爲一個二進制的字符串
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
// 是否是最後一個。
boolean flag = (i == bytes.length - 1);
sb.append(byteToBitStr(!flag, b));
}
// 把字符串按照指定的赫夫曼編碼進行解碼
// 把赫夫曼編碼的鍵值對進行調換
Map<String, Byte> map = new HashMap<>();
for (Map.Entry<Byte, String> entry : huffCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
// 創建一個集合,用於存byte
List<Byte> list = new ArrayList<>();
// 處理字符串
for (int i = 0; i < sb.length();) {
int count = 1;
boolean flag = true;
Byte b = null;
// 截取出一個byte
while (flag) {
String key = sb.substring(i, i + count);
b = map.get(key);
if (b == null) {
count++;
} else {
flag = false;
}
}
list.add(b);
i += count;
}
// 把集合轉爲數組
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
/**
* 轉爲8位的字符串
* @param flag
* @param b
* @return
*/
private static String byteToBitStr(boolean flag, byte b) {
int temp = b;
if (flag) {
temp |= 256;//按位或 256
}
String str = Integer.toBinaryString(temp);//返回int變量的二進制表示的字符串。
if (flag) {
return str.substring(str.length() - 8);
} else {
return str;
}
}
}
P47-4.20 使用赫夫曼編碼壓縮文件
運行項目後,選中項目,進行刷新。
package demo10;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestHuffmanCode45 {
public static void main(String[] args) {
// String msg = "can you can a can as a can canner can a can.";
// /**
// * 赫夫曼編碼 不直接對字符串進行編碼。而是編碼字符串的byte數組。
// * 所有的數據都可以轉換爲byte數組。文件壓縮使用byte數組。
// * 文件讀取出來是一個byte數組。所以,將數據轉換爲byte數組。
// */
// byte[] bytes = msg.getBytes();
// //進行赫夫曼編碼壓縮
// byte[] b = huffmanZip(bytes);// 獲取使用赫夫曼編碼壓縮後的byte數組
// System.out.println(bytes.length);
// System.out.println(b.length);
// //使用赫夫曼編碼進行解碼
// byte[] newBytes = decode(huffCodes,b);
// System.out.println(new String(newBytes));
String src = "1.png";
String dest = "2.zip";
try{
zipFile(src, dest);
} catch(IOException e){
e.printStackTrace();
}
}
/**
* 壓縮文件
*
* @param src
* @param dst
* @throws IOException
*/
public static void zipFile(String src, String dst) throws IOException {
// 創建一個輸入流
InputStream is = new FileInputStream(src);
// 創建一個和輸入流指向的文件大小一樣的byte數組
byte[] b = new byte[is.available()];//available()獲取讀的文件所有的字節個數
// 讀取文件內容
is.read(b);
is.close();
// 使用赫夫曼編碼進行編碼
byte[] byteZip = huffmanZip(b);
System.out.println(b.length);
System.out.println(byteZip.length);
// 輸出流
OutputStream os = new FileOutputStream(dst);
ObjectOutputStream oos = new ObjectOutputStream(os);
// 把壓縮後的byte數組寫入文件
oos.writeObject(byteZip);
// 把赫夫曼編碼表寫入文件
oos.writeObject(huffCodes);
oos.close();
os.close();
}
/**
* 進行赫夫曼編碼壓縮的方法
*
* @param bytes
* @return
*/
private static byte[] huffmanZip(byte[] bytes) {
// 1、先統計每一個byte出現的次數,並放入一個集合中【getNodes()】
List<Node> nodes = getNodes(bytes);
// 2、創建一棵赫夫曼樹
Node tree = createHuffmanTree(nodes);
System.out.println(tree);
System.out.println(tree.right);
System.out.println(tree.left);
// 3、創建一個赫夫曼編碼表
Map<Byte, String> huffCodes = getCodes(tree);
System.out.println(huffCodes);
// 4、編碼
byte[] b = zip(bytes, huffCodes);
return b;
}
/**
* 1、把byte數組轉爲node集合
*
* @param bytes
* @return
*/
private static List<Node> getNodes(byte[] bytes) {
List<Node> nodes = new ArrayList<>();
// 存儲每一個byte出現了多少次。
Map<Byte, Integer> counts = new HashMap<>();
// 統計每一個byte出現的次數
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
//System.out.println(counts);
//{32=11, 97=11, 114=1, 99=7, 115=1, 117=1, 101=1, 121=1, 110=8, 46=1, 111=1}
// 32-空格、97-a
// 把每一個鍵值對轉爲一個node對象
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
/**
* 2、創建赫夫曼樹
*
* @param nodes
* @return
*/
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
// 排序
Collections.sort(nodes);
// System.out.println(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);
}
// 用於臨時存儲路徑
static StringBuilder sb = new StringBuilder();
// 用於存儲赫夫曼編碼
static Map<Byte, String> huffCodes = new HashMap<>();
/**
* 3、根據赫夫曼樹獲取赫夫曼編碼
* 需要保存前面樹支的結果0、1
* @param tree
* @return
*/
private static Map<Byte, String> getCodes(Node tree) {
if (tree == null) {
return null;
}
getCodes(tree.left, "0", sb);
getCodes(tree.right, "1", sb);
return huffCodes;
}
private static void getCodes(Node node, String code, StringBuilder sb) {
StringBuilder sb2 = new StringBuilder(sb);
sb2.append(code);
if (node.data == null) {
getCodes(node.left, "0", sb2);
getCodes(node.right, "1", sb2);
} else {
huffCodes.put(node.data, sb2.toString());
}
}
/**
* 4、進行赫夫曼編碼
*
* @param bytes
* @param huffCodes2
* @return
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffCodes) {
StringBuilder sb = new StringBuilder();
// 把需要壓縮的byte數組處理成一個二進制的字符串
for (byte b : bytes) {
sb.append(huffCodes.get(b));
}
// System.out.println(sb.toString());
// 定義長度
int len;
if (sb.length() % 8 == 0) {
len = sb.length() / 8;
} else {
len = sb.length() / 8 + 1;
}
// 用於存儲壓縮後的byte
byte[] by = new byte[len];
// 記錄新byte的位置
int index = 0;
for (int i = 0; i < sb.length(); i += 8) {
String strByte;
if (i + 8 > sb.length()) {
strByte = sb.substring(i);
} else {
strByte = sb.substring(i, i + 8);
}
byte byt = (byte) Integer.parseInt(strByte, 2);//二進制轉十進制
System.out.println(strByte + " : " + byt);
by[index] = byt;
index++;
}
return by;
}
/**
* 使用指定的赫夫曼編碼表進行解碼
*
* @param huffCodes2
* @param b
* @return
*/
private static byte[] decode(Map<Byte, String> huffCodes, byte[] bytes) {
StringBuilder sb = new StringBuilder();
// 把byte數組轉爲一個二進制的字符串
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
// 是否是最後一個。
boolean flag = (i == bytes.length - 1);
sb.append(byteToBitStr(!flag, b));
}
// 把字符串按照指定的赫夫曼編碼進行解碼
// 把赫夫曼編碼的鍵值對進行調換
Map<String, Byte> map = new HashMap<>();
for (Map.Entry<Byte, String> entry : huffCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
// 創建一個集合,用於存byte
List<Byte> list = new ArrayList<>();
// 處理字符串
for (int i = 0; i < sb.length();) {
int count = 1;
boolean flag = true;
Byte b = null;
// 截取出一個byte
while (flag) {
String key = sb.substring(i, i + count);
b = map.get(key);
if (b == null) {
count++;
} else {
flag = false;
}
}
list.add(b);
i += count;
}
// 把集合轉爲數組
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
/**
* 轉爲8位的字符串
* @param flag
* @param b
* @return
*/
private static String byteToBitStr(boolean flag, byte b) {
int temp = b;
if (flag) {
temp |= 256;//按位或 256
}
String str = Integer.toBinaryString(temp);//返回int變量的二進制表示的字符串。
if (flag) {
return str.substring(str.length() - 8);
} else {
return str;
}
}
}
P48-4.21 使用赫夫曼編碼解壓文件
package demo10;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestHuffmanCode45 {
public static void main(String[] args) {
// String msg = "can you can a can as a can canner can a can.";
// /**
// * 赫夫曼編碼 不直接對字符串進行編碼。而是編碼字符串的byte數組。
// * 所有的數據都可以轉換爲byte數組。文件壓縮使用byte數組。
// * 文件讀取出來是一個byte數組。所以,將數據轉換爲byte數組。
// */
// byte[] bytes = msg.getBytes();
// //進行赫夫曼編碼壓縮
// byte[] b = huffmanZip(bytes);// 獲取使用赫夫曼編碼壓縮後的byte數組
// System.out.println(bytes.length);
// System.out.println(b.length);
// //使用赫夫曼編碼進行解碼
// byte[] newBytes = decode(huffCodes,b);
// System.out.println(new String(newBytes));
String src = "1.png";
String dest = "2.zip";
// try {
// zipFile(src, dest);
// } catch (IOException e) {
// e.printStackTrace();
// }
try {
unZip("2.zip", "3.png");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件的解壓
*
* @param src
* @param dst
* @throws Exception
*/
public static void unZip(String src, String dst) throws Exception {
// 創建一個輸入流
InputStream is = new FileInputStream("2.zip");
ObjectInputStream ois = new ObjectInputStream(is);
// 讀取byte數組
byte[] b = (byte[]) ois.readObject();
// 讀取赫夫曼編碼表
Map<Byte, String> codes = (Map<Byte, String>) ois.readObject();
ois.close();
is.close();
// 解碼
byte[] bytes = decode(codes, b);
// 創建一個輸出流
OutputStream os = new FileOutputStream(dst);
// 寫出數據
os.write(bytes);
os.close();
}
/**
* 壓縮文件
*
* @param src
* @param dst
* @throws IOException
*/
public static void zipFile(String src, String dst) throws IOException {
// 創建一個輸入流
InputStream is = new FileInputStream(src);
// 創建一個和輸入流指向的文件大小一樣的byte數組
byte[] b = new byte[is.available()];//available()獲取讀的文件所有的字節個數
// 讀取文件內容
is.read(b);
is.close();
// 使用赫夫曼編碼進行編碼
byte[] byteZip = huffmanZip(b);
System.out.println(b.length);
System.out.println(byteZip.length);
// 輸出流
OutputStream os = new FileOutputStream(dst);
ObjectOutputStream oos = new ObjectOutputStream(os);
// 把壓縮後的byte數組寫入文件
oos.writeObject(byteZip);
// 把赫夫曼編碼表寫入文件
oos.writeObject(huffCodes);
oos.close();
os.close();
}
/**
* 進行赫夫曼編碼壓縮的方法
*
* @param bytes
* @return
*/
private static byte[] huffmanZip(byte[] bytes) {
// 1、先統計每一個byte出現的次數,並放入一個集合中【getNodes()】
List<Node> nodes = getNodes(bytes);
// 2、創建一棵赫夫曼樹
Node tree = createHuffmanTree(nodes);
System.out.println(tree);
System.out.println(tree.right);
System.out.println(tree.left);
// 3、創建一個赫夫曼編碼表
Map<Byte, String> huffCodes = getCodes(tree);
System.out.println(huffCodes);
// 4、編碼
byte[] b = zip(bytes, huffCodes);
return b;
}
/**
* 1、把byte數組轉爲node集合
*
* @param bytes
* @return
*/
private static List<Node> getNodes(byte[] bytes) {
List<Node> nodes = new ArrayList<>();
// 存儲每一個byte出現了多少次。
Map<Byte, Integer> counts = new HashMap<>();
// 統計每一個byte出現的次數
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
//System.out.println(counts);
//{32=11, 97=11, 114=1, 99=7, 115=1, 117=1, 101=1, 121=1, 110=8, 46=1, 111=1}
// 32-空格、97-a
// 把每一個鍵值對轉爲一個node對象
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
/**
* 2、創建赫夫曼樹
*
* @param nodes
* @return
*/
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
// 排序
Collections.sort(nodes);
// System.out.println(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);
}
// 用於臨時存儲路徑
static StringBuilder sb = new StringBuilder();
// 用於存儲赫夫曼編碼
static Map<Byte, String> huffCodes = new HashMap<>();
/**
* 3、根據赫夫曼樹獲取赫夫曼編碼
* 需要保存前面樹支的結果0、1
* @param tree
* @return
*/
private static Map<Byte, String> getCodes(Node tree) {
if (tree == null) {
return null;
}
getCodes(tree.left, "0", sb);
getCodes(tree.right, "1", sb);
return huffCodes;
}
private static void getCodes(Node node, String code, StringBuilder sb) {
StringBuilder sb2 = new StringBuilder(sb);
sb2.append(code);
if (node.data == null) {
getCodes(node.left, "0", sb2);
getCodes(node.right, "1", sb2);
} else {
huffCodes.put(node.data, sb2.toString());
}
}
/**
* 4、進行赫夫曼編碼
*
* @param bytes
* @param huffCodes2
* @return
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffCodes) {
StringBuilder sb = new StringBuilder();
// 把需要壓縮的byte數組處理成一個二進制的字符串
for (byte b : bytes) {
sb.append(huffCodes.get(b));
}
// System.out.println(sb.toString());
// 定義長度
int len;
if (sb.length() % 8 == 0) {
len = sb.length() / 8;
} else {
len = sb.length() / 8 + 1;
}
// 用於存儲壓縮後的byte
byte[] by = new byte[len];
// 記錄新byte的位置
int index = 0;
for (int i = 0; i < sb.length(); i += 8) {
String strByte;
if (i + 8 > sb.length()) {
strByte = sb.substring(i);
} else {
strByte = sb.substring(i, i + 8);
}
byte byt = (byte) Integer.parseInt(strByte, 2);//二進制轉十進制
System.out.println(strByte + " : " + byt);
by[index] = byt;
index++;
}
return by;
}
/**
* 使用指定的赫夫曼編碼表進行解碼
*
* @param huffCodes2
* @param b
* @return
*/
private static byte[] decode(Map<Byte, String> huffCodes, byte[] bytes) {
StringBuilder sb = new StringBuilder();
// 把byte數組轉爲一個二進制的字符串
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
// 是否是最後一個。
boolean flag = (i == bytes.length - 1);
sb.append(byteToBitStr(!flag, b));
}
// 把字符串按照指定的赫夫曼編碼進行解碼
// 把赫夫曼編碼的鍵值對進行調換
Map<String, Byte> map = new HashMap<>();
for (Map.Entry<Byte, String> entry : huffCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
// 創建一個集合,用於存byte
List<Byte> list = new ArrayList<>();
// 處理字符串
for (int i = 0; i < sb.length();) {
int count = 1;
boolean flag = true;
Byte b = null;
// 截取出一個byte
while (flag) {
String key = sb.substring(i, i + count);
b = map.get(key);
if (b == null) {
count++;
} else {
flag = false;
}
}
list.add(b);
i += count;
}
// 把集合轉爲數組
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
/**
* 轉爲8位的字符串
* @param flag
* @param b
* @return
*/
private static String byteToBitStr(boolean flag, byte b) {
int temp = b;
if (flag) {
temp |= 256;//按位或 256
}
String str = Integer.toBinaryString(temp);//返回int變量的二進制表示的字符串。
if (flag) {
return str.substring(str.length() - 8);
} else {
return str;
}
}
}
11111111111111111~