哈夫曼树的简介与创建

本文详细介绍了哈夫曼树的概念,包括路径和路径长度、结点的权和带权路径长度等,并提供了哈夫曼树的构建步骤和相应Java代码。通过排序、合并最小结点的方式构建最优二叉树,最终用于哈夫曼编码的生成,适用于数据压缩。
摘要由CSDN通过智能技术生成

哈夫曼树

哈夫曼树的几个重要概念

1)路径和路径长度:在一棵树中,从一个结点可以到达孩子结点或孙子结点之间的通路,叫做路径,通路中分支的数目成为路径的长度。若规定根结点的层数为1,则从根结点到第n层的路径长度为 n - 1。

2)结点的权及带权路径长度:若赋给树种结点一个含有某种意义的数值,则这个数值称为结点的权。结点的带权路径长度为:从根结点到该结点的路径长度与权的乘积。

3)树的带权路径长度:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL(weighted path length),权值越大的结点离根结点越近的二叉树才是最优二叉树。

4)WPL最小的就是哈夫曼树

哈夫曼树的构造

步骤:
1)从小到大排序,将每一个结点即数据看成是一个最小的二叉树;
2)取出权值最小的两个二叉树;
3)组成一个新的二叉树,该树的权值为取出的两个二叉树权值之和;
4)将新的二叉树的权值放入数组,然后重复 1 2 3 4。直到所有的数据都被处理

相应代码:

public class HuffmanTree {
    public TreeNode creatHuffman(int[] nums){
        ArrayList<TreeNode> arr = new ArrayList<>();
        for(int i : nums){
            arr.add(new TreeNode(i));
        }
        while(arr.size() > 1){
            //1.让结点按照权值从小到大排序
            arr.sort(new Comparator<TreeNode>() {
                @Override
                public int compare(TreeNode o1, TreeNode o2) {
                    return o1.val - o2.val;
                }
            });
            //2.取出权值最小的两个结点
            TreeNode left = arr.get(0);
            TreeNode right = arr.get(1);
            arr.remove(1);//取出的元素记得删除
            arr.remove(0);
            //3.创建一个新的结点,该结点权值为取出的权值之和,并把取出的两个结点作为此结点的左右结点
            TreeNode parent = new TreeNode(left.val + right.val);
            parent.left = left;
            parent.right = right;
            //4.将新结点加入数组,重复1 2 3 4
            arr.add(parent);
        }
        return arr.get(0);
    }
    static class TreeNode{
        int val;
        TreeNode left;
        TreeNode right;
        public TreeNode(int val) {
            this.val = val;
        }
        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    '}';
        }
    }
}

哈夫曼编码

基本介绍

哈夫曼编码是一种编码方式,是一种程序算法。
哈夫曼编码是哈夫曼树再电信通讯中的经典应用之一。
哈夫曼编码广泛应用与压缩之中,压缩率通常在20%~90%之间。
哈夫曼编码是可变字长编码(VLC)的一种。

代码实现获取哈夫曼编码

上面我们已经说过如何建立一个哈夫曼树,这里以字符串的压缩为例,首先创建一个方法获取结点的数组以便于创建哈夫曼树。结点的组成为一个Byte和一个int型的weight代表权重即不同字符出现的次数,要统计次数我们用到了map。

    static class Node{
        Node left;
        Node right;
        int weight;
        Byte data;
        public Node(Byte data,int weight) {
            super();
            this.weight = weight;
            this.data = data;
        }
        @Override
        public String toString() {
            return "Node [data=" + data + ", weight=" + weight + "]";
        }
    }
    //统计数据中每个字符出现的次数,将次数作为它们各自的权值
    public ArrayList<Node> getNodes(byte[] bytes){
        //ArrayList保存每一个结点,用来创建哈夫曼树
        ArrayList<Node> arr = new ArrayList<>();
        //map用来统计每一个字符出现的次数
        HashMap<Byte,Integer> map = new HashMap<>();
        for(Byte b : bytes){
            if(map.containsKey(b)){
                map.put(b,map.get(b) + 1);
            }else{
                map.put(b, 1);
            }
        }
        //统计完以后加入ArrayList
        for(Map.Entry<Byte,Integer> entry: map.entrySet()){
            arr.add(new Node(entry.getKey(), entry.getValue()));
        }
        return arr;
    }

获取到List之后就创建哈夫曼树。

    //返回一个哈夫曼树的根结点
    public Node creatHuffmanTree(ArrayList<Node> arr){
        while(arr.size() > 1){
            //1.讲结点从小到达排序
            arr.sort(new Comparator<Node>(){
                @Override
                public int compare(Node o1, Node o2) {
                    // TODO Auto-generated method stub
                    return o1.weight - o2.weight;
                }
            });
            //2.取出权值最小的两个结点,记得删除
            Node left = arr.get(0);
            Node right = arr.get(1);
            arr.remove(1);
            arr.remove(0);
            //3.创建新结点
            Node parent = new Node(null, left.weight + right.weight);
            parent.left = left;
            parent.right = right;
            arr.add(parent);
            //4.重复直到全部数据处理完成
        }
        return arr.get(0);
    }

最后一步就是返回每个字符代表的哈夫曼编码,这样成对出现的数据使用一个map型静态变量存放。

    private static HashMap<Byte,String> hashMap = new HashMap<>();
    /**
     * 获取不同字符对应的哈夫曼编码,并放入map中
     * @param root 根结点
     * @param type 判断是左结点还是右结点
     */
    public void getCodes(Node root,String type,StringBuilder s){
        if(root == null) return;
        StringBuilder s1 = new StringBuilder(s);
        s1.append(type);
        if(root.data == null){
            getCodes(root.left,"0",s1);
            getCodes(root.right,"1",s1);
        }else {//如果不为空,则表明是叶子节点,加入map中
            hashMap.put(root.data,s1.toString());
        }

    }
    public HashMap<Byte,String> getCodes(Node root){
        getCodes(root,"",new StringBuilder());
        return hashMap;
    }

最后放完整的实现类

public class HuffmanCode {
    public static void main(String[] args) {
        String s = "hello,world";
        byte[] bytes = s.getBytes();
        HuffmanCode h = new HuffmanCode();
        ArrayList<Node> arr = h.getNodes(bytes);
        Node root = h.creatHuffmanTree(arr);
        System.out.println();
        System.out.println(h.getCodes(root));
    }

    //统计数据中每个字符出现的次数,将次数作为它们各自的权值
    public ArrayList<Node> getNodes(byte[] bytes){
        //ArrayList保存每一个结点,用来创建哈夫曼树
        ArrayList<Node> arr = new ArrayList<>();
        //map用来统计每一个字符出现的次数
        HashMap<Byte,Integer> map = new HashMap<>();
        for(Byte b : bytes){
            if(map.containsKey(b)){
                map.put(b,map.get(b) + 1);
            }else{
                map.put(b, 1);
            }
        }
        //统计完以后加入ArrayList
        for(Map.Entry<Byte,Integer> entry: map.entrySet()){
            arr.add(new Node(entry.getKey(), entry.getValue()));
        }
        return arr;
    }

    //返回一个哈夫曼树的根结点
    public Node creatHuffmanTree(ArrayList<Node> arr){
        while(arr.size() > 1){
            //1.讲结点从小到达排序
            arr.sort(new Comparator<Node>(){
                @Override
                public int compare(Node o1, Node o2) {
                    // TODO Auto-generated method stub
                    return o1.weight - o2.weight;
                }
            });
            //2.取出权值最小的两个结点,记得删除
            Node left = arr.get(0);
            Node right = arr.get(1);
            arr.remove(1);
            arr.remove(0);
            //3.创建新结点
            Node parent = new Node(null, left.weight + right.weight);
            parent.left = left;
            parent.right = right;
            arr.add(parent);
            //4.重复直到全部数据处理完成
        }
        return arr.get(0);
    }



    private static HashMap<Byte,String> hashMap = new HashMap<>();
    /**
     * 获取不同字符对应的哈夫曼编码,并放入map中
     * @param root 根结点
     * @param type 判断是左结点还是右结点
     */
    public void getCodes(Node root,String type,StringBuilder s){
        if(root == null) return;
        StringBuilder s1 = new StringBuilder(s);
        s1.append(type);
        if(root.data == null){
            getCodes(root.left,"0",s1);
            getCodes(root.right,"1",s1);
        }else {//如果不为空,则表明是叶子节点,加入map中
            hashMap.put(root.data,s1.toString());
        }

    }
    public HashMap<Byte,String> getCodes(Node root){
        getCodes(root,"",new StringBuilder());
        return hashMap;
    }


    static class Node{
        Node left;
        Node right;
        int weight;
        Byte data;
        public Node(Byte data,int weight) {
            super();
            this.weight = weight;
            this.data = data;
        }
        @Override
        public String toString() {
            return "Node [data=" + data + ", weight=" + weight + "]";
        }
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值