哈夫曼编码

哈夫曼又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种
该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。
作用:即利用哈夫曼算法的理论构建出字符char和对字符进行编码code,的一个二叉树的字典 如:a->101

其主要原理是:

1、通过对需要编码的字符串resource中字符char的出现频率进行统计得出相应的映射关系,
2、依据字符出现的频率对字符char进行编码 code
3、构建哈夫曼二叉树HuffmanTree(二叉树的节点data(char(字符串中出现的每一个char)、weight字符出现的权重、left,right左右子节点、节点data的对应编码code)),且二叉树的排序的规则以字符出现的权重进行排序
4、将字符串resource 和 HuffmanTree 的字符编码字典进行字符的迭代编码从而形成一串01的字符串
5、解码的过程是通过将01的串进行迭代,并构建一个临时的缓存区st,将011010…串的字符进行st入栈,并将st的串和HuffmanTree 中节点的code进行编码比对,当比对结果是true时,则表示当前的code对应是节点的data,从而将st清空,并继续对0110001…进行迭代解码

实例
统计字符的概率
字符 概率
a0.12
b0.40
c0.15
d0.05
e0.25
字符编码

我们现在要将文本编码成0/1序列从而使得计算机能够进行读取和计算。为了保证每个字符的独一性,所以我们给予不同的的字符以不同的编码。如果给每个字符赋予等长的编码的话,会使得平均的编码长度过长,影响计算时的性能,浪费计算机的资源(定长编码的缺点)。这时我们就想到了变长编码,理所当然的,给出现概率较大的字符赋予##### 统计字符的概率,概率较小的字符赋予较长的编码,这样在计算的时候不就可以节省很多时间了吗?可这样我们又面临到了一个巨大的问题,我们来看下面这种情况,我们对字符进行编码:

字符概率编码
a0.1201
b0.400
c0.1500
d0.0510
e0.251
构建哈夫曼树

假设现在文本中的字符是bcd,转换之后的0/1序列为00010,可我们要在转换成文本的时候究竟是把第一位的0读作b还是把前两位的00读作c呢?为了解决这个问题,就又有了前缀码的概念。顾名思义,前缀码的含义就是任意字符的编码都不是其他字符编码的前缀。那么该如何形成前缀码呢?首先我们要构造一棵二叉树,指向左孩子的"边"记作0,指向右孩子的点记作“1”,叶子节点为代编码的字符,出现概率越大的字符离根的距离就越近。
在这里插入图片描述

代码实现
定义树的节点
public class Node<T> implements Comparable<Node<T>> {
    private T data;
    private double weight;
    private Node<T> left;
    private Node<T> right;
    String code;

    public Node(T data, double weight){
        this.data = data;
        this.weight = weight;
        this.code = "";

    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public Node<T> getLeft() {
        return left;
    }

    public void setLeft(Node<T> left) {
        this.left = left;
    }

    public Node<T> getRight() {
        return right;
    }

    public void setRight(Node<T> right) {
        this.right = right;
    }

    public String getCode(){
        return code;
    }

    public void setCode(String str){
        code = str;
    }

    @Override
    public String toString(){
        return "data:"+this.data+";weight:"+this.weight+";code: "+this.code;
    }

    @Override
    public int compareTo(Node<T> other) {
        if(other.getWeight() > this.getWeight()){
            return 1;
        }
        if(other.getWeight() < this.getWeight()){
            return -1;
        }

        return 0;
    }
}
定义哈夫曼树。(主要包含两个方法createTree(),breadth())

createTree方法返回一个结

  • createTree方法返回一个结点,也就是根结点。首先把所有的nodes结点类都储存在一个List中,利用Collections的sort方法把结点按照权值的大小按照从大到小的顺序进行排列。然后把List中的倒数第二个元素设为左孩子,倒数第一个元素设为右孩子。这个时候要注意:它们的双亲结点为以左右孩子的权值的和作为权值的构成的新的结点。然后删去左右孩子结点,将形成的新结点加入的List中。直到List中只剩下一个结点,也就是根结点时为止
  • 广度遍历的方法,在遍历的时候,每遍历到左孩子,就把结点中的code变量加上“0”,这里的加不是简单的数学运算,而是字符串的叠加。每遍历到右孩子,就把结点中的code变量加上“1”,这样遍历过一遍后,叶子结点中的code储存的就是对应的哈夫曼编码值。
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Queue;

public class HuffmanTree {
    /**
     * 构建哈夫曼树
     * @param nodes
     * @return
     */
    public <T> Node<T> createTree(List<Node<T>> nodes) {
        while (nodes.size() > 1) {
            Collections.sort(nodes);

            Node<T> left = nodes.get(nodes.size() - 2);
            left.setCode(0 + "");
            Node<T> right = nodes.get(nodes.size() - 1);
            right.setCode(1 + "");
            Node<T> parent = new Node<T>(null, left.getWeight() + right.getWeight());
            parent.setLeft(left);
            parent.setRight(right);
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

    /**
     * 在构建哈夫曼树的类中还实现了一个广度遍历的方法,在遍历的时候,每遍历到左孩子,就把结点中的code变量加上“0”,这里的加不是简单的数学运算,而是字符串的叠加。每遍历到右孩子,就把结点中的code变量加上“1”,这样遍历过一遍后,叶子结点中的code储存的就是对应的哈夫曼编码值。
     * @param root
     * @return
     */
    public <T> List<Node<T>> breadth(Node<T> root) {
        List<Node<T>> list = new ArrayList<Node<T>>();
        Queue<Node<T>> queue = new ArrayDeque<Node<T>>();

        if (root != null) {
            queue.offer(root);
            root.getLeft().setCode(root.getCode() + "0");
            root.getRight().setCode(root.getCode() + "1");
        }

        while (!queue.isEmpty()) {
            list.add(queue.peek());
            Node<T> node = queue.poll();
            if (node.getLeft() != null)
                node.getLeft().setCode(node.getCode() + "0");
            if (node.getRight() != null)
                node.getRight().setCode(node.getCode() + "1");

            if (node.getLeft() != null) {
                queue.offer(node.getLeft());
            }

            if (node.getRight() != null) {
                queue.offer(node.getRight());
            }
        }
        return list;
    }
}
处理需要编码的字符串
public static class readtxt {
        char[] chars = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s'
            ,'t','u','v','w','x','y','z','P',' '};
        int[] number = new int[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        public String txtString(File file){
            StringBuilder result = new StringBuilder();
            try{
                BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
                String s = null;
                while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                    result.append(System.lineSeparator()+s);
                    num(s);
                }
                br.close();
            }catch(Exception e){
                e.printStackTrace();
            }
            return result.toString();
        }
        
		public String txtString(String reusrce){
            StringBuilder result = new StringBuilder();
            try{
                BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
                num(reusrce);
            }catch(Exception e){
                e.printStackTrace();
            }
            return result.toString();
        }

        public void num(String string){
            for(int i = 0;i<28;i++){
                int temp = 0;
                for(int j = 0;j<string.length();j++){
                    if(string.charAt(j) == chars[i])
                        temp++;
                }
                number[i] += temp;
            }
        }

        public int[] getNumber(){
            return number;
        }

        public char[] getChars(){
            return chars;
        }
    }
测试方法
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

public class TestMain {

    public static void main(String[] args) {
//        File file = new File("F:\\input1\\input1.txt");

        //编码的原始数据
        String temp = "please go to the attendance management to update in time when the national holidays change";

        //文本进行处理,定义两个数组获得文本中出现的字符和字符出现的次数
        readtxt read = new readtxt();
//        String temp = read.txtString(file);
        System.out.println(temp);
        System.out.println("***************");
        int[] num = read.getNumber();
        char[] chars = read.getChars();

        //利用一个循环把对应的data值和weight权重值构造成结点加入到list中。
        List<Node<String>> list = new ArrayList<>();
        for(int i = 0;i<28;i++){
            System.out.print(chars[i]+":"+num[i]+"   ");
            list.add(new Node<String>(chars[i]+"",num[i]));
        }
        //构建哈夫曼树并得到根结点。
        HuffmanTree huffmanTree = new HuffmanTree();
        Node<String> root = huffmanTree.createTree(list);

        List<Node<String>> list2 = new ArrayList<>();
        list2=huffmanTree.breadth(root);

        List<String> list3 = new ArrayList<>(list2.size());
        List<String> list4 = new ArrayList<>(list2.size());
        for(int i = 0;i<list2.size();i++){
            if(list2.get(i).getData()!=null) {
                Node<String> node = list2.get(i);
                list3.add(node.getData());
                list4.add(node.getCode());
            }
        }

        String result = "";
        for(int i = 0;i<temp.length();i++){
            for(int j = 0;j<list3.size();j++){
                if(temp.charAt(i) == list3.get(j).charAt(0)){
                    result += list4.get(j);
                }
            }
        }

        System.out.println("哈夫曼算法编码结果");
        System.out.println(result);

        List<String> list5 = new ArrayList<>(result.length());
        for(int i = 0;i<result.length();i++){
            list5.add(result.charAt(i)+"");
        }

        String temp2 = "";
        String temp3 = "";
        while (list5.size()>0){
            temp2 = temp2+"" +list5.get(0);
            list5.remove(0);
            for(int i=0;i<list4.size();i++){
                if (temp2.equals(list4.get(i))) {
                    temp3 = temp3+""+list3.get(i);
                    temp2 = "";
                }
            }
        }

        System.out.println("***********");
        System.out.println(temp3);
    }
}
运行结果

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 哈夫曼编码是一种使用变长编码来减少数据传输量的有效方法。在 Verilog 中,我们可以通过以下步骤实现哈夫曼编码。 首先,我们需要构建哈夫曼编码树。这棵树是由输入数据的频率构建而成的。可以使用哈夫曼树构建算法,该算法会根据输入数据的频率构造出最优的哈夫曼编码树。 然后,我们需要根据构建好的哈夫曼编码树生成对应的哈夫曼编码表。哈夫曼编码表将字符与对应的二进制编码一一对应起来。可以使用深度优先搜索的方法遍历哈夫曼编码树,生成哈夫曼编码表。 接下来,我们可以编写 Verilog 代码来实现哈夫曼编码。首先,我们需要定义输入数据的接口,并接收输入数据。然后,根据输入的字符,查询哈夫曼编码表,将对应的二进制编码输出。 最后,我们需要将输出的二进制编码进行传输。为了正确传输数据,我们需要在输出数据前加入标志位,表示输出数据的开始位置。在接收端,需要识别这个标志位,并将后续的二进制编码解码为对应的字符。 总结来说,哈夫曼编码在 Verilog 中的实现需要构建哈夫曼编码树,生成哈夫曼编码表,并编写相应的编码和解码逻辑。这样可以实现对输入数据的高效压缩和解压缩。 ### 回答2: 哈夫曼编码是一种基于字符频率来构建编码的最优前缀编码方法。在Verilog中实现哈夫曼编码可以分为两步:构建哈夫曼树和生成编码表。 首先,构建哈夫曼树。我们可以使用二叉树的数据结构来表示哈夫曼树。在Verilog中,可以通过定义一个节点结构体来表示二叉树节点,其中包括字符和频率信息,以及左右子节点指针。通过比较字符频率来构建哈夫曼树,可以采用贪心算法,每次选择频率最小的两个节点合并为一个新节点,直到只剩下一个节点为止。 接下来,生成编码表。通过遍历哈夫曼树,可以得到每个字符的编码。在Verilog中,可以使用递归或者迭代的方式进行树的遍历。当遍历到叶子节点时,记录下路径上的0和1,即可得到每个字符的哈夫曼编码。可以使用一个数据结构来保存字符与编码的对应关系,比如使用一个二维数组或者哈希表。 最后,将哈夫曼编码应用于实际数据压缩或传输中。通过将原始数据按照对应的编码进行替换或者添加额外的标识,可以实现压缩和解压缩的功能。 总之,通过Verilog实现哈夫曼编码需要先构建哈夫曼树,然后生成编码表,最后将编码应用于数据压缩或传输中。这是一个相对复杂的任务,需要熟悉Verilog语言和数据结构的相关知识才能完成。 ### 回答3: 哈夫曼编码是一种变长编码的压缩算法,常用于将频率较高的字符用较短的编码表示,从而减小存储或传输的数据量。为了实现哈夫曼编码,可以使用硬件描述语言Verilog来设计相应的电路。 实现哈夫曼编码的Verilog电路需要以下几个主要模块: 1. 频率统计模块:用于统计输入文本中各个字符的频率。输入文本可以通过数据输入端口传入,使用计数器来统计每个字符出现的次数。 2. 构建哈夫曼树模块:根据字符频率构建哈夫曼树。使用二叉堆等数据结构来优化树的构建过程,按照频率大小进行排序。 3. 哈夫曼编码生成模块:根据构建好的哈夫曼树,生成每个字符对应的哈夫曼编码串。可以使用递归或者迭代的方式遍历哈夫曼树,同时记录每个字符的编码。 4. 编码器模块:将输入的文本按照生成的哈夫曼编码进行编码。通过读取输入文本的每个字符,并查找对应的哈夫曼编码,输出对应的编码串。 5. 译码器模块:将编码后的二进制串按照哈夫曼编码进行译码,得到原始文本。根据哈夫曼编码树进行译码,从根节点开始依次查找对应的字符。 以上模块可以通过组合逻辑电路来实现,可以利用状态机等技术进行控制。此外,还需要提供测试模块,用于验证设计的正确性。 总的来说,通过使用Verilog语言来设计实现哈夫曼编码的电路,可以实现对输入文本进行压缩和解压缩的功能。这种硬件实现可以提高编码运行速度,并减小对系统资源的占用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值