数据结构Java06【赫夫曼树、概述、原理分析、代码实现(数据压缩、创建编码表、解码、压缩文件、解压文件)】
學習地址:【數據結構與算法基礎-java版】? ? ? ? ? ? ? ? ??🚀數據結構--Java專欄🚀
- 筆記01【01-09】【概述、數組基本使用】【源碼、課件】
- 筆記02【10-18】【棧、隊列、單鏈表(增刪節點)、循環鏈表、雙向循環鏈表、遞歸(斐波那契、漢諾塔)】
- 筆記03【19-27】【(時間、空間復雜度);八大排序(冒泡、快速、插入、希爾、選擇、歸并、基數、隊列基數)】
- 筆記04【28-33】【樹結構(二叉樹)概述、創建、遍歷、查找節點、刪除節點】
- 筆記05【34-39】【順序存儲二叉樹概述、二叉樹遍歷、堆排序、線索二叉樹實現及遍歷】
- 筆記06【40-48】【赫夫曼樹、概述、原理分析、代碼實現(數據壓縮、創建編碼表、解碼、壓縮文件、解壓文件)】
- 筆記07【49-54】【二叉排序樹(添加、查找、刪除節點)】
- 筆記08【55-57】【二叉平衡樹(AVL)-概述、單旋轉、雙旋轉】
- 筆記09【58-60】【計算機中數據的存儲原理、2-3樹的插入原理、B樹和B+樹】
- 筆記10【61-63】【哈希表概述、散列函數的設計、散列沖突解決方案】
- 筆記11【64-67】【圖結構概述、圖遍歷原理(BFS\DFS)、圖遍歷代碼實現】
目? ?錄
P40-4.13 赫夫曼樹概述
P41-4.14 創建赫夫曼樹的流程分析
P42-4.15 代碼實現創建赫夫曼樹
1、Node.java
2、TestHuffmanTree.java
P43-4.16 赫夫曼編碼原理分析
1、通信領域中信息的處理1-定長編碼
2、通信領域中信息的處理2-非定長編碼
3、通信領域中信息的處理3-赫夫曼編碼
P44-4.17 數據壓縮之創建赫夫曼樹
1、Map從入門到性能分析
2、思路分析
3、代碼實現
3.1、Node.java
3.2、TestHuffmanCode44.java
P45-4.18 數據壓縮之創建編碼表&編碼
1、TestHuffmanCode45.java
P46-4.19 使用赫夫曼編碼進行解碼
P47-4.20 使用赫夫曼編碼壓縮文件
P48-4.21 使用赫夫曼編碼解壓文件
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;}@Overridepublic int compareTo(Node o) {return -(this.value - o.value);// 加“-”,大在前}// Alt + Shift + S【快捷鍵】@Overridepublic 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)轉換為數字?!綾-->99,a-->97】
【計算機只能識別0、1串,每一個數字都是一個byte(字節)】
再將 數字 轉換為 8位的字節,
【1100011--->99--->c】
將01串發給另一臺計算機B,計算機B按 8位 劃分 01串,
將 01串 轉換為 十進制數字,再將?十進制數字 根據 ASCII?轉換為 字母。
?
定長編碼方式,傳輸效率 極低!!!
固定長度~浪費空間 ==>?非定長編碼
https://tool.lu/hexconvert/
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;// 存儲節點代表的字符/英文字符可轉byteint weight;// 權值Node left;// 左節點Node right;// 右節點// 創建赫夫曼樹的時候,新創建的節點,無data,只有weight|data可為空public Node(Byte data, int weight) {this.data = data;this.weight = weight;}@Overridepublic String toString() {return "Node [data=" + data + ", weight=" + weight + "]";}@Overridepublic 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;}// 用于存儲壓縮后的bytebyte[] 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;}// 用于存儲壓縮后的bytebyte[] 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());}// 創建一個集合,用于存byteList<Byte> list = new ArrayList<>();// 處理字符串for (int i = 0; i < sb.length();) {int count = 1;boolean flag = true;Byte b = null;// 截取出一個bytewhile (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;}// 用于存儲壓縮后的bytebyte[] 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());}// 創建一個集合,用于存byteList<Byte> list = new ArrayList<>();// 處理字符串for (int i = 0; i < sb.length();) {int count = 1;boolean flag = true;Byte b = null;// 截取出一個bytewhile (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;}// 用于存儲壓縮后的bytebyte[] 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());}// 創建一個集合,用于存byteList<Byte> list = new ArrayList<>();// 處理字符串for (int i = 0; i < sb.length();) {int count = 1;boolean flag = true;Byte b = null;// 截取出一個bytewhile (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~
總結
以上是生活随笔為你收集整理的数据结构Java06【赫夫曼树、概述、原理分析、代码实现(数据压缩、创建编码表、解码、压缩文件、解压文件)】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端之旅,正式启航~【前端学习路线图+配
- 下一篇: 数据结构Java07【二叉排序树(添加查