Java数据结构与算法(赫夫曼树,赫夫曼编码)

赫夫曼树

基本介绍:
(1)给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,城这样的二叉树为最优二叉树,也称为哈夫曼树(HuffmanTree)
(2)赫夫曼树是带权路径最短的树,权值较大的结点离根较近
赫夫曼树几个重要的概念:
(1)路径和路径长度:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称之为路径,通路中分支的数目称之为路径长度,若规定根结点的层数为1,则从根接地那啊到底L层结点的路径长度为L-1。
(2)结点的权以及带权路径长度:若将树中的结点赋给一个有着某种含义的数值,则这个数值称之为该节点的权。结点的带权路径长度为:从根结点到该节点之间的路径长度与该结点的权的乘积
(3)树的带权路径长度:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL(weight path length),权值越大的结点离根结点越近的二叉树才是最优二叉树
(4)WPL最小的就是赫夫曼树(如下图中间的就是赫夫曼树)
在这里插入图片描述

将一个数列构建赫夫曼树的步骤
(1)从小到大进行排序,每个数据看成是一个结点,每个结点可以看成是一棵最简单的二叉树
(2)取出根结点权值最小的两棵二叉树
(3)组成一棵新的二叉树,该新的二叉树根结点的权值是前面两棵二叉树根结点权值的和
(4)在将这棵新的二叉树,以根结点的权值大小再次排序,不断重复1-2-3-4的步骤,知道数列中,所有的数据都被处理,就得到了一棵赫夫曼树

代码实现:

public class HuffManTreeDemo {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    13,7,8,3,29,6,1};
        HuffNode temp = createHuffManTree(arr);
        perOrder(temp);

    }
    

    //前序遍历赫夫曼树
    public static void perOrder(HuffNode root) {
    
    
        if(root == null) {
    
    
            System.out.println("赫夫曼树是空的");
            return ;
        }
        root.preOrder();
    }


    //创建赫夫曼树
    public static HuffNode createHuffManTree(int[] arr) {
    
    

        //将数组放到HuffNode结点中
        ArrayList<HuffNode> list = new ArrayList<>();
        for(int i = 0;i < arr.length-1;i++) {
    
    
            list.add(new HuffNode(arr[i]));
        }

        //对集合进行循环取值,构造赫夫曼树
        while(list.size() > 1) {
    
    
            //重新对集合里面的数据进行排序
            Collections.sort(list);
            //取出集合里面最小的两个值
            HuffNode node1 = list.get(0);
            HuffNode node2 = list.get(1);

            //将这两个结点合并成一个二叉树
            //创建父亲结点
            HuffNode parent = new HuffNode(node1.value+node2.value);
            parent.left = node1;
            parent.right = node2;

            //将刚刚取出的两个结点从集合里面删除
            list.remove(node1);
            list.remove(node2);

            //将创建好的二叉树的根结点返回到集合里面
            list.add(parent);
        }
        return list.get(0);
    }
}

//为了能让Node实现Collection集合排序,让HuffNode实现Comparable接口
class HuffNode implements Comparable<HuffNode> {
    
    
    int value;
    HuffNode left;
    HuffNode right;

    public HuffNode(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(HuffNode o) {
    
    
        //从小到大排序
        return this.value - o.value;
    }
}

赫夫曼编码

(1)赫夫曼编码也翻译为哈夫曼编码(Huffman Coding),是一种编码方式,属于一种程序设计算法
(2)赫夫曼编码是赫夫曼树在电讯通信中的经典的应用之一 (3)赫夫曼编码广泛用于数据文件压缩,其压缩率通常在20%~90%之间
(4)赫夫曼码是可变字长编码(VLC)的一种,被称之为最佳编码
哈夫曼编码就是利用赫夫曼树,将字符串中每个字符出现的次数和字符装装到一个结点,并且以每个字符出现的次数来将他们构造成一棵哈夫曼树,在通过根节点到指定结点的关键路径来确定每个字符的编码是多少

将字符串“i like like like java do you like a java”,用哈夫曼编码的方式给他进行压缩
代码实现:

public class HuffmanCodeDemo {
    
    
    public static void main(String[] args) {
    
    
        String s = "i like like like java do you like a java";
        byte[] sByte = s.getBytes();
        HuffCodeNode temp = createHuffManTree(getHuffCode(sByte));
        temp.preOrder();
        HuffmanCode(temp,"",stringBuilder);
    }

 //创建一个map用于存放哈夫曼编码表
    static  Map<Byte,String> huffmanCode = new HashMap<Byte,String>();
    //创建一个StringBuilder用于拼接哈夫曼 编码
    static StringBuilder stringBuilder = new StringBuilder();
    //创建哈夫曼编码表,用于查找每个字符用什么来代替
    public static void HuffmanCode(HuffCodeNode node,String code,StringBuilder stringBuilder) {
    
    
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        //先将现在的位置记下来
        stringBuilder1.append(code);
        //确定这个点是不是需要放到表里的点,data为0的点就说明它不是我们之前存储的字符,所以就不存它
        if(node.data == 0) {
    
    
            //往这个点的左边去找叶子结点,因为只有叶子结点才是我们之前放在哈夫曼树里的字符
            HuffmanCode(node.left,"0",stringBuilder1);
            //往这个点的右边去找叶子结点
            HuffmanCode(node.right,"1",stringBuilder1);
        } else {
    
    //如果这个点的data有值,那么它就是我们之前存进去的字符,就要把他存进哈夫曼表里
            huffmanCode.put(node.data,stringBuilder1.toString());
        }
    }
    //将字节数组装换成HuffCodeNode集合
    public static ArrayList<HuffCodeNode> getHuffCode(byte[] arr) {
    
    
        ArrayList<HuffCodeNode> list = new ArrayList<>();
        //计算每个字节出现的次数,将这个次数作为创建哈夫曼树的权值
        Map<Byte,Integer> map = new HashMap<>();
        for(int i = 0; i < arr.length;i++) {
    
    
            Integer number = map.get(i);
            if(number == null) {
    
    
                map.put(arr[i],1);
            }else {
    
    
                map.put(arr[i],number+1);
            }
        }
        //将每个字节以及它出现的次数装进HuffCodeNode结点
        for(Map.Entry<Byte,Integer> entry : map.entrySet()) {
    
    
            HuffCodeNode temp = new HuffCodeNode(entry.getKey(),entry.getValue());
            list.add(temp);
        }
        return list;
    }

    //前序遍历赫夫曼树
    public static void perOrder(HuffCodeNode root) {
    
    
        if(root == null) {
    
    
            System.out.println("赫夫曼树是空的");
            return ;
        }
        root.preOrder();
    }


    //创建赫夫曼树
    public static HuffCodeNode createHuffManTree(ArrayList<HuffCodeNode> list) {
    
    

        //对集合进行循环取值,构造赫夫曼树
        while(list.size() > 1) {
    
    
            //重新对集合里面的数据进行排序
            Collections.sort(list);
            //取出集合里面最小的两个值
            HuffCodeNode node1 = list.get(0);
            HuffCodeNode node2 = list.get(1);

            //将这两个结点合并成一个二叉树
            //创建父亲结点
            HuffCodeNode parent = new HuffCodeNode(node1.number+node2.number);
            parent.left = node1;
            parent.right = node2;

            //将刚刚取出的两个结点从集合里面删除
            list.remove(node1);
            list.remove(node2);

            //将创建好的二叉树的根结点返回到集合里面
            list.add(parent);
        }
        return list.get(0);
    }
}

//为了能让Node实现Collection集合排序,让HuffNode实现Comparable接口
class HuffCodeNode implements Comparable<HuffCodeNode> {
    
    
    int number;
    byte data;
    HuffCodeNode left;
    HuffCodeNode right;

    public HuffCodeNode(int number) {
    
    
        this.number = number;

    }
    public HuffCodeNode(byte data,int number) {
    
    
        this.number = number;
        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 String toString() {
    
    
        return "Node[data="+ data +",value="+ number+"]";
    }
    @Override
    //重写排序的关键函数
    public int compareTo(HuffCodeNode o) {
    
    
        //从小到大排序
        return this.number - o.number;
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_48627750/article/details/121378829