数据结构与算法(十)霍夫曼编码

霍夫曼编码

package algorithm;

import java.util.*;

public class HuffmanEncode {


    public static void main(String[] args) {

        String str = "i like like like algorithm";

        byte[] res = huffmanZip(str);

    }

    //压缩数据
    private static byte[] huffmanZip(String data){
        byte[] bytes = data.getBytes();
        //遍历bytes,并统计每个byte出现的次数
        Map<Byte, Integer> bytesMap = new HashMap();
        for (byte b : bytes) {
            if (bytesMap.containsKey(b)) {
                Integer count = bytesMap.get(b);
                bytesMap.put(b, count + 1);
            } else {
                bytesMap.put(b, 1);
            }
        }

        //为每个byte创建Node节点
        List<Node> nodes = new ArrayList<>();
        for (Map.Entry<Byte, Integer> element : bytesMap.entrySet()) {
            nodes.add(new Node(element.getValue(), element.getKey()));
        }

        //创建霍夫曼数
        Node root = generateHuffmanTree(nodes);
        preOrder(root);

        //建立霍夫曼编码表
        Map<Byte, String> huffmanCodesMap = new HashMap<>();
        generateHuffmanCode(root, "", "", huffmanCodesMap);
        System.out.println("Huffman code : " + huffmanCodesMap);

        //生成压缩的霍夫曼编码
        byte[] huffmanCodesByte = zip(bytes, huffmanCodesMap);
        System.out.println(Arrays.toString(huffmanCodesByte));

        return huffmanCodesByte;
    }

    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodesMap) {
        StringBuffer sb = new StringBuffer();
        //遍历数组,得到经过霍夫曼编码表转换后的二进制的编码字符串
        for (byte b : bytes) {
            sb.append(huffmanCodesMap.get(b));
        }

        //将编码每8位比拿成一个字符
        int len = (sb.length() + 7) / 8;
        byte[] huffmanCodesByte = new byte[len];

        for (int i = 0, index = 0; i < sb.length(); i += 8, index++) {
            String temp;
            if(i + 8 > sb.length()){
                temp = sb.substring(i);
            }else{
                temp = sb.substring(i, i+8);
            }
            huffmanCodesByte[index] = (byte) Integer.parseInt(temp,2);
        }
        return huffmanCodesByte;
    }


    /**
     * @param node            传入的节点
     * @param path            路径:左子节点是0, 右子节点1
     * @param code            用于拼接路径
     * @param huffmanCodesMap 用于存放生成的霍夫曼编码
     */
    private static void generateHuffmanCode(Node node, String path, String code, Map<Byte, String> huffmanCodesMap) {
        if (node == null) {
            return;
        }
        String newCode = code + path;
        if (node.data == null) {//此节点是非叶子节点
            //向左递归, 路径加上"0"
            if (node.left != null) {
                generateHuffmanCode(node.left, "0", newCode, huffmanCodesMap);
            }
            //向右递归,路径加上"1"
            if (node.right != null) {
                generateHuffmanCode(node.right, "1", newCode, huffmanCodesMap);
            }
        } else {//此节点是叶子节点, 将对应的哈夫曼编码加入huffmanCodesMap
            huffmanCodesMap.put(node.data, newCode);
        }

    }


    //创建霍夫曼数
    private static Node generateHuffmanTree(List<Node> nodes) {
        if (nodes.size() == 0) {
            return null;
        }

        //循环
        //当数组仅剩一个元素时,该元素则为霍夫曼数的根节点
        while (nodes.size() > 1) {
            //1. 对数组进行排序
            Collections.sort(nodes);
            //2.取出权值最小的两个节点作为左右节点,两个权值之和作为新的父节点
            Node left = nodes.get(0);
            Node right = nodes.get(1);

            Node parent = new Node(left.value + right.value, null);

            parent.left = left;
            parent.right = right;

            //从数组中删除前两个节点,并将新的父节点添加进数组中
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

    public static void preOrder(Node node) {
        if (node != null) {
            node.preOrder();
        } else {
            System.out.println("Tree is empty.");
        }
    }

}


class Node implements Comparable<Node> {
    int value; //节点的权值
    Byte data;//存储数据
    Node left;
    Node right;

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

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

        if (this.right != null) {
            this.right.preOrder();
        }
    }

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

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                ", data=" + data +
                '}';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值