数据结构:哈夫曼树、哈夫曼编码

哈夫曼树

 

 

import java.util.ArrayList;
import java.util.Collections;

public class HuffmanTree {
    public static void main(String[] args) {
        int[] arr = {13, 7, 8, 3, 29, 6, 1};
        Node root = creatHuffmanTree(arr);
        preOrderTree(root);
        //Node{weight=67, flag=false}
        //Node{weight=29, flag=true}
        //Node{weight=38, flag=false}
        //Node{weight=15, flag=false}
        //Node{weight=7, flag=true}
        //Node{weight=8, flag=true}
        //Node{weight=23, flag=false}
        //Node{weight=10, flag=false}
        //Node{weight=4, flag=false}
        //Node{weight=1, flag=true}
        //Node{weight=3, flag=true}
        //Node{weight=6, flag=true}
        //Node{weight=13, flag=true}
    }


    //创建一颗哈夫曼树
    public static Node creatHuffmanTree(int[] arr) {
        ArrayList<Node> arrayList = new ArrayList<>();
        for (int no : arr) {
            Node node = new Node(no,true);
            arrayList.add(node);
        }
        while (arrayList.size() > 1) {
            //由小到大排序
            Collections.sort(arrayList);
            //取出最小的两个节点
            Node leftNode = arrayList.get(0);
            Node rightNode = arrayList.get(1);
            //构建一个新的二叉树
            Node parent = new Node(leftNode.weight + rightNode.weight,false);
            parent.left = leftNode;
            parent.right = rightNode;
            //修改集合元素
            arrayList.remove(leftNode);
            arrayList.remove(rightNode);
            arrayList.add(parent);
        }
        return arrayList.get(0);
    }

    //前序遍历
    public static void preOrderTree(Node root) {
        if (root != null) {
            root.preOrder();
        }else{
            System.out.println("该树为空");
        }
    }
}
public class Node implements Comparable<Node> {
    int weight;
    boolean flag;//是否为叶子节点
    Node left;
    Node right;

    public Node(int weight, boolean flag) {
        this.weight = weight;
        this.flag = flag;
    }

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

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

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

哈夫曼编码

 编码流程:

 

数据压缩

 

 

public class Node {
    Byte date;
    int weight;
    Node left;
    Node right;

    public Node() {
    }

    public Node(Byte date, int weight) {
        this.date = date;
        this.weight = weight;
    }

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

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

}
import java.util.*;

public class HuffmanTree_Compress {
    public static void main(String[] args) {
        String s = "i like like like java do you like a java";
        //压缩前的字节数组
        byte[] bt1 = s.getBytes();
        System.out.println(Arrays.toString(bt1) + "---" + bt1.length);
        /*
        [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]---40
         */

        /**
         * 压缩
         */
        //1..获取Node节点的集合
        ArrayList<Node> nodes = getNodes(bt1);

        //2.生成哈夫曼树
        Node root = createHuffmanTree(nodes);

        //3.生成编码表
        HashMap<Byte, String> encodingTable = getCode(root);
        System.out.println(encodingTable);
        //{32=000, 97=011, 100=1000, 117=1010, 101=1111, 118=010, 121=1001, 105=1101, 106=001, 107=1110, 108=1100, 111=1011}

        //4.压缩后的字节数组
        byte[] bt2 = compress(bt1, encodingTable);
        /*
        110100011001101111011110001100110111101111000110011011110111100000101101001
        1000100010110001001101110100001100110111101111000011000001011010011
         */
        System.out.println(Arrays.toString(bt2) + "---" + bt2.length);
        //[-47, -101, -34, 51, 123, -58, 111, 120, 45, 49, 22, 38, -24, 102, -9, -122, 11, 76]---18

        /**
         * 解压
         */
        String res = deCompress(bt2);
        System.out.println(res);

    }


    //1.获取Node节点的集合
    public static ArrayList<Node> getNodes(byte[] bt) {
        ArrayList<Node> nodes = new ArrayList<>();
        //统计每个字符出现的次数,存在HashMap中
        HashMap<Byte, Integer> map = new HashMap<>();
        int i = 0;
        for (byte date : bt) {
            if (map.get(date) == null) {
                map.put(date, i);
            } else {
                i++;
                map.put(date, i);
            }
        }
        //遍历HashMap,创建Node对象,并添加到ArrayList中
        Set<Byte> set = map.keySet();
        for (Byte b : set) {
            Node node = new Node(b, map.get(b));
            nodes.add(node);
        }
        return nodes;
    }

    //2.创建哈夫曼树
    public static Node createHuffmanTree(ArrayList<Node> nodes) {
        //将Nodes按照权重排序
        while (nodes.size() > 1) {
            Collections.sort(nodes, new Comparator<Node>() {
                @Override
                public int compare(Node o1, Node o2) {
                    return o1.weight - o2.weight;
                }
            });
            //取出最小的两个节点
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //创建父节点
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //调整nodes集合
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

    //3.获取编码表
    static HashMap<Byte, String> encodingTable = new HashMap<>();
    static StringBuilder sb = new StringBuilder();

    public static HashMap<Byte, String> getCode(Node root) {
        if (root != null) {
            getCode(root, "", sb);
        }
        return encodingTable;
    }

    public static void getCode(Node node, String code, StringBuilder sb) {
        StringBuilder cur = new StringBuilder(sb);
        cur.append(code);
        if (node.date == null) {
            getCode(node.left, "0", cur);
            getCode(node.right, "1", cur);
        } else {
            encodingTable.put(node.date, cur.toString());
        }
    }


    //4.根据编码表,压缩字节数组
    public static byte[] compress(byte[] bt1, HashMap<Byte, String> encodingTable) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bt1) {
            String s = encodingTable.get(b);
            sb.append(s);
        }
        System.out.println(sb.toString());
        //将字符串的长度补全为8的整数倍
        if (sb.length() % 8 != 0) {
            int n = 8 - sb.length() % 8;
            for (int i = 0; i < n; i++) {
                sb.append("0");
            }
        }
        //字符串转字节数组
        byte[] res = new byte[sb.length() / 8];
        int index = 0;
        for (int i = 0; i < sb.length(); i += 8) {
            String substring = sb.substring(i, i + 8);
            byte b = (byte) Integer.parseInt(substring, 2);//二进制转十进制
            res[index] = b;
            index++;
        }
        return res;
    }

    //5.解压
    public static String deCompress(byte[] bt2) {
        //十进制转二进制
        StringBuilder sb = new StringBuilder();
        for (byte b : bt2) {
            int temp = b;
            temp |= 256;//或运算,只要有一个为1就为1,用于补高位
            String str = Integer.toBinaryString(temp);
            str = str.substring(str.length() - 8);
            sb.append(str);
        }
        //反转编码表
        HashMap<String, Byte> reverseEncodingTable = new HashMap<>();
        Set<Byte> set = encodingTable.keySet();
        for (Byte b : set) {
            String s = encodingTable.get(b);
            reverseEncodingTable.put(s, b);
        }
        //用编码表匹配
        ArrayList<Byte> arrayList = new ArrayList();
        int i = 0;
        int count = 1;
        while (i + count < sb.length()) {//!!!要考虑最后补零的情况
            String temp = sb.substring(i, i + count);
            if (reverseEncodingTable.containsKey(temp)) {
                arrayList.add(reverseEncodingTable.get(temp));
                i += count;
                count = 1;
            } else {
                count++;
            }
        }
        //遍历arrayList
        StringBuilder res = new StringBuilder();
        for (int x = 0; x < arrayList.size(); x++) {
            byte temp = arrayList.get(x);
            res.append((char) temp);
        }
        return res.toString();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值