Huffman樹和編解碼

Huffman樹的建立

基本介紹
  1. 給定n個權值作為n 個葉子結點毕荐,構造一棵二叉樹舀患,若該樹的帶權路徑長度(wpl)達到最小,稱這樣的二叉樹為最優(yōu)二叉樹,也稱為哈夫曼樹(Huffman Tree)
  2. 赫夫曼樹是帶權路徑長度最短的樹程腹,權值較大的結點離根較近
赫夫曼樹幾個重要概念
  1. 路徑和路徑長度:在一棵樹中锰瘸,從一個結點往下可以達到的孩子或孫子結點之間的通路,稱為路徑蹬碧。通路中分支的數(shù)目稱為路徑長度舱禽。若規(guī)定根結點的層數(shù)為1,則從根結點到第L層結點的路徑長度為L-1
  2. 結點的權及帶權路徑長度:若將樹中結點賦給一個有著某種含義的數(shù)值.則這個數(shù)值稱為該結點的權恩沽。結點的帶權路徑長度為:從根結點到該結點之間的路徑長度與該結點的權的乘積
  3. 樹的帶權路徑長度:樹的帶權路徑長度規(guī)定為所有葉子結點的帶權路徑長度之和誊稚,記為WPL(weighted path length) ,權值越大的結點離根結點越近的二叉樹才是最優(yōu)二叉樹
  4. WPL最小的就是赫夫曼樹
赫夫曼樹創(chuàng)建思路

給你一個數(shù)列{13,7,8,3,29,6,1},要求轉成一顆赫夫曼樹.構成赫夫曼樹的步驟:

  1. 從小到大進行排序,將每一個數(shù)據(jù)罗心,每個數(shù)據(jù)都是一個節(jié)點片吊,每個節(jié)點可以看成是一顆最簡單的二叉樹
  2. 取出根節(jié)點權值最小的兩顆二叉樹
  3. 組成一顆新的二叉樹,該新的二叉樹的根節(jié)點的權值是前面兩顆二叉樹根節(jié)點權值的和
  4. 再將這顆新的二叉樹,以根節(jié)點的權值大小再次排序.不斷重復1-2-3-4 的步驟协屡,直到數(shù)列中俏脊,所有的數(shù)據(jù)都被處理,就得到一顆赫夫曼樹
public class HuffmanTree {
    public static void main(String[] args) {
        int[] arr={13,7,8,3,29,6,1};
        Node huffmanTree=createHuffmanTree(arr);
        preOrder(huffmanTree);
    }

    //前序遍歷
    public static void preOrder(Node root){
        if(root!=null)
            root.preOrder();
        else
            System.out.println("空樹,不能遍歷");
    }

    public static Node createHuffmanTree(int[] arr){
        List<Node> nodes=new ArrayList<>();
        for(int value:arr)
            nodes.add(new Node(value));
        while(nodes.size()>1)
        {
            Collections.sort(nodes);
            Node leftNode=nodes.get(0);
            Node rightNode=nodes.get(1);
            Node parentNode=new Node(leftNode.value+rightNode.value);
            parentNode.left=leftNode;
            parentNode.right=rightNode;
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parentNode);
        }
        return nodes.get(0);
    }

}
class Node implements Comparable<Node>{
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    //前序遍歷
    public void preOrder(){
        System.out.println(this);
        if(this.left!=null)
            this.left.preOrder();
        if(this.right!=null)
            this.right.preOrder();
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    @Override
    public int compareTo(Node node) {
        return this.value-node.value;
    }
}

赫夫曼編解碼

public class HuffmanCode {
    static Map<Byte,String> huffmanCodes=new HashMap<>();
    static StringBuilder stringBuilder=new StringBuilder();

    public static void main(String[] args) {
        String content="asdds astkk nhb sgacsw aevsbd";
        byte[] contentBytes=content.getBytes();
        byte[] huffmanCodeBytes=huffmanZip(contentBytes);
        System.out.println(Arrays.toString(huffmanCodeBytes));
        byte[] sourceBytes=decode(huffmanCodes,huffmanCodeBytes);
        System.out.println(new String(sourceBytes));

    }

    /**
     * 將一個byte轉化為一個二進制的字符串
     * @param flag 表示是否需要補高位肤晓,如果是true表示需要補高位爷贫,如果是false表示不補认然,如果是最后一個字節(jié)無需補高位
     * @param b
     * @return
     */
    public static String byteToBitString(boolean flag,byte b){
        int temp=b;
        if(flag)
            temp|=256;
        String str=Integer.toBinaryString(temp);
        if(flag)
            return str.substring(str.length()-8);
        else
            return str;
    }

    /**
     * 解碼
     * @param huffmanCodes
     * @param huffmanBytes
     * @return
     */
    public static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
        StringBuilder stringBuilder=new StringBuilder();
        for(int i=0;i<huffmanBytes.length;i++){
            byte b=huffmanBytes[i];
            boolean flag=(i==huffmanBytes.length-1);
            stringBuilder.append(byteToBitString(!flag,b));
        }
        Map<String,Byte> map=new HashMap<>();
        for(Map.Entry<Byte,String>entry:huffmanCodes.entrySet()){
            map.put(entry.getValue(),entry.getKey());
        }

        List<Byte> list=new ArrayList<>();
        for(int i=0;i<stringBuilder.length();){
            int count=1;
            boolean flag=true;
            Byte b=null;
            while(flag){
                String key=stringBuilder.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;
    }
    //封裝流程
    private static byte[] huffmanZip(byte[] bytes){
        List<Node> nodes=getNodes(bytes);
        Node huffmanTreeRoot=createHuffmanTree(nodes);
        Map<Byte,String> huffmanCodes=getCodes(huffmanTreeRoot);
        byte[] huffmanCodeBytes=zip(bytes,huffmanCodes);

        return huffmanCodeBytes;
    }

    /**
     * 一、將傳入的字節(jié)數(shù)組轉為List<Node>集合
     * @param bytes 傳入的字節(jié)數(shù)組
     * @return List<Node>集合
     */
    private static List<Node> getNodes(byte[] bytes){
        ArrayList<Node> nodes=new ArrayList<>();
        Map<Byte,Integer> counts=new HashMap<>();

        for(byte b:bytes) {
            Integer count=counts.get(b);
            if(count==null)
                counts.put(b,1);
            else
                counts.put(b,count+1);
        }

        for(Byte b:counts.keySet()){
            Node node=new Node(b,counts.get(b));
            nodes.add(node);
        }
        return nodes;
    }

    /**
     * 二漫萄、構建Huffman樹
     * @param nodes Node集合
     * @return 根節(jié)點
     */
    public static Node createHuffmanTree(List<Node> nodes){
        while(nodes.size()>1)
        {
            Collections.sort(nodes);
            Node leftNode=nodes.get(0);
            Node rightNode=nodes.get(1);
            Node parentNode=new Node(null,leftNode.weight+rightNode.weight);
            parentNode.left=leftNode;
            parentNode.right=rightNode;

            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parentNode);
        }
        return nodes.get(0);
    }

    //重載
    public static Map<Byte,String> getCodes(Node root){
        if(root==null)
            return null;
        else{
            getCodes(root.left,"0",stringBuilder);
            getCodes(root.right,"1",stringBuilder);
        }
        return huffmanCodes;
    }

    /**
     * 三卷员、獲取Huffman編碼表
     * @param node 節(jié)點
     * @param code 路徑:左子節(jié)點為0,右子節(jié)點為1
     * @param stringBuilder 拼接路徑(編碼)
     */
    public static void getCodes(Node node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2=new StringBuilder(stringBuilder);
        stringBuilder2.append(code);
        if(node!=null){
            if(node.data==null){
                getCodes(node.left,"0",stringBuilder2);
                getCodes(node.right,"1",stringBuilder2);
            }
            else
                huffmanCodes.put(node.data,stringBuilder2.toString());
        }
    }

    /**
     * 四腾务、將原始數(shù)組轉化為壓縮后的字節(jié)數(shù)組
     * @param bytes 原始字節(jié)數(shù)組
     * @param huffmanCodes 編碼表
     * @return 壓縮后的字節(jié)數(shù)組
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
        StringBuilder stringBuilder=new StringBuilder();
        for(byte b:bytes){
            stringBuilder.append(huffmanCodes.get(b));
        }
        int len=(stringBuilder.length()+7)/8;
        byte[] huffmanCodeBytes=new byte[len];
        int index=0;
        for(int i=0;i<stringBuilder.length();i+=8){
            String strByte;
            if(i+8>stringBuilder.length())
                strByte=stringBuilder.substring(i);
            else
                strByte=stringBuilder.substring(i,i+8);
            huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
            index++;
        }
        return huffmanCodeBytes;
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末毕骡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子岩瘦,更是在濱河造成了極大的恐慌未巫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件启昧,死亡現(xiàn)場離奇詭異叙凡,居然都是意外死亡,警方通過查閱死者的電腦和手機密末,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門握爷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人严里,你說我怎么就攤上這事新啼。” “怎么了刹碾?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵燥撞,是天一觀的道長。 經(jīng)常有香客問我教硫,道長,這世上最難降的妖魔是什么辆布? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任瞬矩,我火速辦了婚禮,結果婚禮上锋玲,老公的妹妹穿的比我還像新娘景用。我一直安慰自己,他們只是感情好惭蹂,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布伞插。 她就那樣靜靜地躺著,像睡著了一般盾碗。 火紅的嫁衣襯著肌膚如雪媚污。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天廷雅,我揣著相機與錄音耗美,去河邊找鬼京髓。 笑死,一個胖子當著我的面吹牛商架,可吹牛的內容都是我干的堰怨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛇摸,長吁一口氣:“原來是場噩夢啊……” “哼备图!你這毒婦竟也來了?” 一聲冷哼從身側響起赶袄,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤揽涮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后弃鸦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绞吁,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年唬格,在試婚紗的時候發(fā)現(xiàn)自己被綠了家破。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡购岗,死狀恐怖汰聋,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情喊积,我是刑警寧澤烹困,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站乾吻,受9級特大地震影響髓梅,放射性物質發(fā)生泄漏。R本人自食惡果不足惜绎签,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一枯饿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诡必,春花似錦奢方、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扭勉,卻和暖如春鹊奖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涂炎。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工嫉入, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留焰盗,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓咒林,卻偏偏與公主長得像熬拒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子垫竞,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容