自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java編程內(nèi)功-數(shù)據(jù)結(jié)構(gòu)與算法「赫夫曼編碼」

開發(fā) 后端 算法
本篇繼續(xù)給大家介紹Java編程數(shù)據(jù)結(jié)構(gòu)與算法相關(guān)知識,今天主要介紹關(guān)于赫夫曼編碼的前世今生。

 基本介紹

  • 赫夫曼編碼也翻譯為(哈夫曼編碼)Huffman Coding,又稱為霍夫曼編碼,是一種編碼方式,屬于一種程序算法。
  • 赫夫曼編碼是赫夫曼樹在電訊通訊中的經(jīng)典的應(yīng)用場景之一。
  • 赫夫曼編碼廣泛的用于數(shù)據(jù)文件壓縮。其壓縮率通常在20%~90%之間。
  • 赫夫曼碼是可變字長編碼(VLC)的一種.Hufuuman于1952年提出一種編碼方法,稱之為最佳編碼。

原理剖析

在通信領(lǐng)域中信息的處理方式1:定長編碼

如: i like like like java do you like a java 共40個(gè)字符,包括空格,其對應(yīng)的ASCII碼,與二進(jìn)制編碼如下圖

按照二進(jìn)制來傳遞信息,總的長度是359(包含空格)

在通信領(lǐng)域中信息的處理方式2:變長編碼

i like like like java do you like a java 共40個(gè)字符,包括空格。變長編碼處理如下圖

字符的編碼都不能是其他字符編碼的前綴,符合此要求的編碼叫做前綴編碼,即不能匹配到重復(fù)的編碼。

在通信領(lǐng)域中信息的處理方式3:赫夫曼編碼

i like like like java do you like a java 共40個(gè)字符,包括空格。變長編碼處理如下圖

按照上面字符出現(xiàn)的次數(shù)構(gòu)建一顆赫夫曼樹,次數(shù)作為權(quán)值。

根據(jù)赫夫曼樹,給各個(gè)字符,規(guī)定編碼(前綴編碼),向左的路徑為0 向右的路徑為1:編碼如下:

按照上面的赫夫曼編碼,我們的"i like like like java do you like a java" 字符串對應(yīng)的編碼(注意這里我們使用的無損壓縮)如下圖。

說明:

原來的長度是359,壓縮了(359-133)/359=62.9%

此編碼滿足前綴編碼,即字符的編碼都不能是其他字符編碼的前綴。不會造成匹配的多義性。

赫夫曼編碼是無損壓縮!!

注意:

這個(gè)赫夫曼樹根據(jù)排序方法不同,也可能不一樣,這樣對應(yīng)的赫夫曼編碼也不完全一樣,但是wpl是一樣的,都是最小的,比如我們讓每次生成的新的二叉樹總是排在權(quán)值相同的二叉樹的最后一個(gè),則生成的二叉樹為:

創(chuàng)建對應(yīng)的赫夫曼樹

根據(jù)赫夫曼編碼壓縮數(shù)據(jù)的原理,需要?jiǎng)?chuàng)建"i like like like java do you like a java" 對應(yīng)的赫夫曼樹

思路:

先創(chuàng)建Node節(jié)點(diǎn),Node {data{存放數(shù)據(jù)},weight(權(quán)值),left,right};

得到"i like like like java do you like a java" 對應(yīng)的byte[] 數(shù)組;

編寫一個(gè)方法,將準(zhǔn)備構(gòu)建赫夫曼樹的node節(jié)點(diǎn)放到List集合;

可以通過集合List創(chuàng)建對應(yīng)的赫夫曼樹;

赫夫曼樹應(yīng)用案例

將一串字符串進(jìn)行壓縮與解壓縮

  1. package com.xie.huffmancode; 
  2.  
  3. import java.util.*; 
  4.  
  5. public class HuffmanCode { 
  6.     public static void main(String[] args) { 
  7.         String str = "i like like like java do you like a java"
  8.         byte[] contentBytes = str.getBytes(); 
  9.         System.out.println("contentBytes=" + Arrays.toString(contentBytes)); 
  10.         List<Node> nodes = getNodes(contentBytes); 
  11.  
  12.         //生成赫夫曼樹 
  13.         Node hufffmanTreeRoot = createHufffmanTree(nodes); 
  14.  
  15.         //生成的赫夫曼編碼表 
  16.         getCodes(hufffmanTreeRoot, "", stringBuilder); 
  17.  
  18.         byte[] huffmanCodeBytes = zip(contentBytes, huffmanCodes); 
  19.         System.out.println("huffmanCodeBytes = " + Arrays.toString(huffmanCodeBytes)); 
  20.  
  21.         byte[] decode = decode(huffmanCodes, huffmanCodeBytes); 
  22.         System.out.println("赫夫曼解碼后對應(yīng)的數(shù)組" + new String(decode)); 
  23.  
  24.         /** 
  25.          * contentBytes=[105, 32, 108, 105, 107, 101, 32, 108, 105, 107, 101, 32, 108, 105, 107, 101, 32, 106, 97, 118, 97, 32, 100, 111, 32, 121, 111, 117, 32, 108, 105, 107, 101, 32, 97, 32, 106, 97, 118, 97] 
  26.          * huffmanCodeBytes = [-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28] 
  27.          * 赫夫曼解碼后對應(yīng)的數(shù)組i like like like java do you like a java 
  28.          */ 
  29.  
  30.     } 
  31.  
  32.     //完成數(shù)據(jù)的解壓思路 
  33.     //1.將huffmanCodeBytes[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28] 
  34.     //  重新轉(zhuǎn)成 赫夫曼編碼對應(yīng)的二進(jìn)制字符串"101010001011111111001000101111...." 
  35.     //2.赫夫曼編碼對應(yīng)的二進(jìn)制字符串"101010001011111111001000101111...." => 對照赫夫曼編碼表 => "i like like like java do you like a java" 
  36.  
  37.     /** 
  38.      * 完成對壓縮數(shù)據(jù)的解碼 
  39.      * 
  40.      * @param huffmanCodes 赫夫曼編碼表 
  41.      * @param huffmanBytes 赫夫曼編碼得到的字節(jié)數(shù)組 
  42.      * @return 原來的字符串對應(yīng)的數(shù)組 
  43.      */ 
  44.     public static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) { 
  45.         StringBuilder stringBuilder = new StringBuilder(); 
  46.         for (int i = 0; i < huffmanBytes.length; i++) { 
  47.             //判斷是不是最后一個(gè)字節(jié) 
  48.             boolean flag = (i == huffmanBytes.length - 1); 
  49.             stringBuilder.append(byteToBitString(!flag, huffmanBytes[i])); 
  50.         } 
  51.  
  52.         Map<String, Byte> map = new HashMap<>(); 
  53.         for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) { 
  54.             Byte k = entry.getKey(); 
  55.             String v = entry.getValue(); 
  56.             map.put(v, k); 
  57.         } 
  58.  
  59.         List<Byte> list = new ArrayList<>(); 
  60.         for (int i = 0; i < stringBuilder.length();) { 
  61.             int count = 1; 
  62.             boolean flag = true
  63.             Byte b = null
  64.             while (flag) { 
  65.                 String key = stringBuilder.substring(i, i + count);//i 不動,count移動,直到匹配一個(gè)字符 
  66.                 b = map.get(key); 
  67.                 if (b == null) { 
  68.                     count++; 
  69.                 } else { 
  70.                     flag = false
  71.                 } 
  72.             } 
  73.             list.add(b); 
  74.             i += count
  75.         } 
  76.         byte[] bytes = new byte[list.size()]; 
  77.         for (int i = 0; i < list.size(); i++) { 
  78.             bytes[i] = list.get(i); 
  79.         } 
  80.         return bytes; 
  81.     } 
  82.  
  83.     /** 
  84.      * 將一個(gè)byte 轉(zhuǎn)成一個(gè)二進(jìn)制的字符串 
  85.      * 
  86.      * @param flag 標(biāo)識是否需要補(bǔ)高位,true標(biāo)識需要補(bǔ)高位,如果是false表示不補(bǔ),如果是最后一個(gè)字節(jié),無需補(bǔ)高位 
  87.      * @param b    傳入的byte 
  88.      * @return 該byte對應(yīng)的二進(jìn)制字符串,(注意是按補(bǔ)碼返回) 
  89.      */ 
  90.     public static String byteToBitString(boolean flag, byte b) { 
  91.         //將b 轉(zhuǎn)成 int 
  92.         int temp = b; 
  93.  
  94.         //如果temp是正數(shù)還需要補(bǔ)高位 
  95.         if (flag) { 
  96.             // 按位或 如 256|1=> 1 0000 0000|0000 0001 => 1 0000 0001 
  97.             temp |= 256; 
  98.         } 
  99.  
  100.         //返回的是temp二進(jìn)制的補(bǔ)碼 
  101.         String bitStr = Integer.toBinaryString(temp); 
  102.         if (flag) { 
  103.             //取后8位 
  104.             return bitStr.substring(bitStr.length() - 8); 
  105.         } else { 
  106.             return bitStr; 
  107.         } 
  108.     } 
  109.  
  110.     /** 
  111.      * 封裝原始字節(jié)數(shù)組轉(zhuǎn)赫夫曼字節(jié)數(shù)組 
  112.      * 
  113.      * @param bytes 
  114.      * @return 
  115.      */ 
  116.     public static byte[] huffmanZip(byte[] bytes) { 
  117.         List<Node> nodes = getNodes(bytes); 
  118.  
  119.         //創(chuàng)建赫夫曼樹 
  120.         Node hufffmanTreeRoot = createHufffmanTree(nodes); 
  121.         //生成赫夫曼編碼 
  122.         getCodes(hufffmanTreeRoot, "", stringBuilder); 
  123.         //返回壓縮后的赫夫曼編碼字節(jié)數(shù)組 
  124.         return zip(bytes, huffmanCodes); 
  125.  
  126.     } 
  127.  
  128.     /** 
  129.      * 將字符串對應(yīng)的byte[] 數(shù)組,通過赫夫曼編碼表,返回一個(gè)赫夫曼編碼壓縮后的byte[] 
  130.      * 
  131.      * @param bytes        原始字符串對應(yīng)的byte[] 
  132.      * @param huffmanCodes 生成的赫夫曼編碼 
  133.      * @return 返回赫夫曼編碼處理后的byte[] 
  134.      */ 
  135.     public static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) { 
  136.         //利用huffmanCodes 將 bytes 轉(zhuǎn)成赫夫曼編碼對應(yīng)的字符串 
  137.         StringBuilder stringBuilder = new StringBuilder(); 
  138.         for (byte b : bytes) { 
  139.             stringBuilder.append(huffmanCodes.get(b)); 
  140.         } 
  141.  
  142.         // 將"101010001011111111001000101111...." 轉(zhuǎn)成byte[] 
  143.         // 統(tǒng)計(jì)返回byte[] huffmanCodeBytes 長度 
  144.         int len; 
  145.         if (stringBuilder.length() % 8 == 0) { 
  146.             len = stringBuilder.length() / 8; 
  147.         } else { 
  148.             len = stringBuilder.length() / 8 + 1; 
  149.         } 
  150.         //創(chuàng)建 存儲壓縮后的byte[]數(shù)組 
  151.         byte[] huffmanCodeBytes = new byte[len]; 
  152.         int index = 0; 
  153.         for (int i = 0; i < stringBuilder.length(); i += 8) { 
  154.             String strByte; 
  155.             if (i + 8 > stringBuilder.length()) { 
  156.                 strByte = stringBuilder.substring(i); 
  157.             } else { 
  158.                 strByte = stringBuilder.substring(i, i + 8); 
  159.             } 
  160.             //將strByte 轉(zhuǎn)成一個(gè)byte ,放入到huffmanCodeBytes 
  161.             huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2); 
  162.             index++; 
  163.         } 
  164.         return huffmanCodeBytes; 
  165.     } 
  166.  
  167.     //生成赫夫曼樹對應(yīng)的赫夫曼編碼表 
  168.     //思路: 
  169.     //1. 將赫夫曼編碼表存放在Map<Byte,String>,形式如32->01,97->100... 
  170.     static Map<Byte, String> huffmanCodes = new HashMap<>(); 
  171.     //2. 在生成赫夫曼編碼表時(shí),需要拼接路徑,定義一個(gè)StringBuilder  存儲某個(gè)葉子節(jié)點(diǎn)的路徑 
  172.     static StringBuilder stringBuilder = new StringBuilder(); 
  173.  
  174.     /** 
  175.      * 將傳入的node 節(jié)點(diǎn)的所有葉子的赫夫曼編碼得到,并放入huffmanCodes集合 
  176.      * 
  177.      * @param node          傳入節(jié)點(diǎn) 
  178.      * @param code          路徑:左子節(jié)點(diǎn)是0,右子節(jié)點(diǎn)是1 
  179.      * @param stringBuilder 用于拼接路徑 
  180.      */ 
  181.     public static void getCodes(Node node, String code, StringBuilder stringBuilder) { 
  182.         StringBuilder stringBuilder2 = new StringBuilder(stringBuilder); 
  183.         stringBuilder2.append(code); 
  184.         if (node != null) { 
  185.             //判斷當(dāng)前node 是葉子節(jié)點(diǎn)還是非葉子節(jié)點(diǎn) 
  186.             if (node.data == null) {//非葉子節(jié)點(diǎn) 
  187.  
  188.                 //向左遞歸處理 
  189.                 getCodes(node.left"0", stringBuilder2); 
  190.                 //向右遞歸處理 
  191.                 getCodes(node.right"1", stringBuilder2); 
  192.             } else {//葉子節(jié)點(diǎn) 
  193.                 huffmanCodes.put(node.data, stringBuilder2.toString()); 
  194.             } 
  195.         } 
  196.     } 
  197.  
  198.     //前序遍歷 
  199.     public static void preOrder(Node root) { 
  200.         if (root != null) { 
  201.             root.preOrder(); 
  202.         } else { 
  203.             System.out.println("赫夫曼樹不能為空~~"); 
  204.         } 
  205.     } 
  206.  
  207.     /** 
  208.      * 將字節(jié)數(shù)組轉(zhuǎn)成node集合 
  209.      * 
  210.      * @param bytes 字節(jié)數(shù)組 
  211.      * @return 
  212.      */ 
  213.     public static List<Node> getNodes(byte[] bytes) { 
  214.         ArrayList<Node> nodes = new ArrayList<>(); 
  215.  
  216.         //存儲每個(gè)byte出現(xiàn)的次數(shù) 
  217.         Map<Byte, Integer> counts = new HashMap<>(); 
  218.         for (byte b : bytes) { 
  219.             counts.merge(b, 1, (a, b1) -> a + b1); 
  220.         } 
  221.  
  222.         //把每個(gè)鍵值對轉(zhuǎn)成一個(gè)node對象,并加入到nodes 集合 
  223.         counts.forEach((k, v) -> nodes.add(new Node(k, v))); 
  224.         return nodes; 
  225.     } 
  226.  
  227.     /** 
  228.      * 生成赫夫曼樹 
  229.      * @param nodes 傳入的節(jié)點(diǎn) 
  230.      * @return 
  231.      */ 
  232.     public static Node createHufffmanTree(List<Node> nodes) { 
  233.         while (nodes.size() > 1) { 
  234.             //排序,從小到大 
  235.             Collections.sort(nodes); 
  236.  
  237.             //(1)取出權(quán)值最小的節(jié)點(diǎn)(二叉樹) 
  238.             Node leftNode = nodes.get(0); 
  239.  
  240.             //(2) 取出權(quán)值第二小的節(jié)點(diǎn)(二叉樹) 
  241.             Node rightNode = nodes.get(1); 
  242.             //(3) 構(gòu)建一顆新的二叉樹 
  243.             Node parent = new Node(null, leftNode.weight + rightNode.weight); 
  244.             parent.left = leftNode; 
  245.             parent.right = rightNode; 
  246.  
  247.             //(4) 從ArrayList中刪除處理過的二叉樹 
  248.             nodes.remove(leftNode); 
  249.             nodes.remove(rightNode); 
  250.             //(5) 將parent加入nodes 
  251.             nodes.add(parent); 
  252.         } 
  253.         //nodes 的最后一個(gè)就是赫夫曼樹的root節(jié)點(diǎn) 
  254.         return nodes.get(0); 
  255.     } 
  256.  
  257. //創(chuàng)建Node,帶數(shù)據(jù)和權(quán)值 
  258. class Node implements Comparable<Node> { 
  259.     //存放數(shù)據(jù)本身,比如'a'=>'97',' ' =>'32' 
  260.     Byte data; 
  261.     //權(quán)值,表示字符出現(xiàn)的次數(shù) 
  262.     int weight; 
  263.  
  264.     Node left
  265.     Node right
  266.  
  267.     public Node(Byte data, int weight) { 
  268.         this.data = data; 
  269.         this.weight = weight; 
  270.     } 
  271.  
  272.     public void preOrder() { 
  273.         System.out.println(this); 
  274.         if (this.left != null) { 
  275.             this.left.preOrder(); 
  276.         } 
  277.         if (this.right != null) { 
  278.             this.right.preOrder(); 
  279.         } 
  280.     } 
  281.  
  282.     @Override 
  283.     public int compareTo(Node o) { 
  284.         //從小到大排序 
  285.         return this.weight - o.weight; 
  286.     } 
  287.  
  288.     @Override 
  289.     public String toString() { 
  290.         return "Node{" + 
  291.                 "data=" + data + 
  292.                 ", weight=" + weight + 
  293.                 '}'
  294.     } 

 赫夫曼壓縮文件注意事項(xiàng)

如果文件本身就經(jīng)過壓縮處理的,那么使用赫夫曼編碼再壓縮效率不會有明顯的變化,比如視頻,ppt等文件。

赫夫曼編碼是按字節(jié)來處理的,因此可以處理所有的文件(二進(jìn)制文件,文本文件)

如果一個(gè)文件中的內(nèi)容,重復(fù)的數(shù)據(jù)不多,壓縮效果也不會明顯。

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2021-05-12 09:07:09

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-13 09:37:41

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-09 06:30:32

JAVA數(shù)據(jù)結(jié)構(gòu)算法

2021-03-18 08:44:20

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-23 08:33:22

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-12 09:13:47

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-10 08:42:19

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-17 09:27:36

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-08 06:28:57

JAVA數(shù)據(jù)結(jié)構(gòu)與算法稀疏數(shù)組

2021-04-15 09:36:44

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-16 09:40:52

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-07 09:26:37

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-22 10:07:45

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-14 08:27:40

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-05-13 07:34:56

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-23 09:12:09

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-11 08:53:20

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-03-24 10:41:04

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-05-08 08:28:38

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-27 06:21:29

Java數(shù)據(jù)結(jié)構(gòu)算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號