java 规范霍夫曼编码

        霍夫曼编码是一种无损数据压缩算法,其中数据中的每个字符都分配有可变长度的前缀代码。出现频率最低的字符获得最大代码,出现频率最高的字符获得最小代码。使用这种技术对数据进行编码非常简单且高效。但是,解码使用此技术生成的比特流效率低下。解码器(或解压缩器)需要了解所使用的编码机制,以便将编码数据解码回原始字符。 

        因此,需要将编码过程的信息与编码数据一起作为字符表及其对应代码传递给解码器。在对大量数据进行常规霍夫曼编码时,此表会占用大量内存空间,而且如果数据中存在大量唯一字符,则由于存在代码本,压缩(或编码)数据大小会增加。因此,为了使解码过程在计算上高效,同时仍保持良好的压缩率,引入了规范霍夫曼码。 

        在标准霍夫曼编码中,使用为每个符号生成的标准霍夫曼代码的位长。首先根据符号的位长按非递减顺序对符号进行排序,然后根据每个位长按字典顺序对符号进行排序。第一个符号获得一个全为零且长度与原始位长相同的代码。对于后续符号,如果符号的位长等于前一个符号的位长,则将前一个符号的代码加一并分配给当前符号。 

        否则,如果符号的位长大于前一个符号的位长,则在增加前一个符号的代码后,将零附加到该代码上,直到长度等于当前符号的位长,然后将代码分配给当前符号。 
此过程继续处理其余符号。 

以下示例说明了该过程:

考虑以下数据: 

特点频率
A10
b1
C15
d7

生成的标准霍夫曼码的位长度为:  

特点霍夫曼编码位长度
A112
b1003
C01
d1013
  • 步骤 1:根据位长对数据进行排序,然后根据每个位长度按字典顺序对符号进行排序。 
特点位长度
C1
A2
b3
d3
  • 步骤 2:为第一个符号的代码分配与位长相同数量的“0”。 
    ‘c’的代码:0
    下一个符号‘a’的位长为 2 > 前一个符号‘c’的位长为 1。将前一个符号的代码增加 1 并附加 (2-1)=1 个零并将代码分配给‘a’。 
    ‘a’的代码:10
    下一个符号‘b’的位长为 3 > 前一个符号‘a’的位长为 2。将前一个符号的代码增加 1 并附加 (3-2)=1 个零并将代码分配给‘b’。 
    ‘b’的代码:110
    下一个符号‘d’的位长为 3 = 前一个符号‘b’的位长为 3。将前一个符号的代码增加 1 并将其分配给‘d’。 
    ‘d’的代码:111
  • 步骤3:最终结果。  
特点规范霍夫曼编码
C0
A10
b110
d111

        该方法的基本优点是,传递给解码器的编码信息可以更紧凑、更节省内存。例如,可以简单地将字符或符号的位长度传递给解码器。由于长度是连续的,因此可以轻松根据长度生成规范代码。 
有关使用 Huffman 树生成 Huffman 代码的信息,请参阅之前的文章如下:

c语言:c语言 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)_霍夫曼的贪婪c语言-优快云博客

c++:c++ 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)_霍夫曼的贪婪算法设计核心代码-优快云博客

c#:C# 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)-优快云博客 

c++ STL:c++ STL 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)-优快云博客 

java:java 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)-优快云博客 

python:python 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)-优快云博客 

javascript:JavaScript 霍夫曼编码 | 贪婪算法(Huffman Coding | Greedy Algo)-优快云博客 

方法:一种简单有效的方法是为数据生成一棵哈夫曼树,并使用类似于 Java 中的 TreeMap 的数据结构来存储符号和位长,以使信息始终保持排序。然后可以使用增量和按位左移运算来获取规范代码。 

执行: 

import java.util.*;

// Node class to store data and its frequency
class Node implements Comparable<Node> {
    char data;
    int freq;
    Node left, right;

    // Constructor
    Node(char c, int f) {
        data = c;
        freq = f;
        left = right = null;
    }

    // Comparator: less_than
    public int compareTo(Node other) {
        return this.freq - other.freq;
    }
}

public class CanonicalHuffman {
    // Function to generate Huffman codes
    static void codeGen(Node root, StringBuilder codeLength, Map<Integer, List<Character>> codeMap) {
        if (root == null) return;
        if (root.left == null && root.right == null) {
            codeMap.computeIfAbsent(codeLength.length(), k -> new ArrayList<>()).add(root.data);
            return;
        }
        codeGen(root.left, codeLength.append('0'), codeMap);
        codeLength.deleteCharAt(codeLength.length() - 1);
        codeGen(root.right, codeLength.append('1'), codeMap);
        codeLength.deleteCharAt(codeLength.length() - 1);
    }

    // Main function implementing Huffman coding
    static void testCanonicalHC(char[] charArr, int[] freq) {
        // Priority queue to store heap tree
        PriorityQueue<Node> q = new PriorityQueue<>();
        for (int i = 0; i < charArr.length; i++) {
            q.add(new Node(charArr[i], freq[i]));
        }

        while (q.size() > 1) {
            Node left = q.poll();
            Node right = q.poll();
            Node merged = new Node('-', left.freq + right.freq);
            merged.left = left;
            merged.right = right;
            q.add(merged);
        }

        Node root = q.poll();
        Map<Integer, List<Character>> codeMap = new HashMap<>();
        codeGen(root, new StringBuilder(), codeMap);

        // Generate Canonical Huffman codes
        Map<Character, String> canonicalMap = new TreeMap<>();
        int cCode = 0;
        for (int length : new TreeSet<>(codeMap.keySet())) {
            List<Character> chars = codeMap.get(length);
            for (char ch : chars) {
                canonicalMap.put(ch, String.format("%" + length + "s", Integer.toBinaryString(cCode++)).replace(' ', '0'));
            }
            cCode <<= 1;
        }

        // Print Canonical Huffman codes
        for (char ch : canonicalMap.keySet()) {
            System.out.println(ch + ": " + canonicalMap.get(ch));
        }
    }

    // Driver code
    public static void main(String[] args) {
        char[] charArr = {'a', 'b', 'c', 'd'};
        int[] freq = {10, 1, 15, 7};
        testCanonicalHC(charArr, freq);
    }
}

输出:
c:0
a:10
b:110
d:111  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

csdn_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值