繁体   English   中英

Java中的霍夫曼树

[英]Huffman Tree in Java

我的霍夫曼树代码有问题。 在main方法中,我输入了符号字符串,还输入了包含符号频率的Integer数组。 它应该打印出每个Symbol及其Huffman代码,但是我认为它是错误的...

这是代码:

 package huffman;

import java.util.*;

abstract class HuffmanTree implements Comparable<HuffmanTree> {
    public final int frequency; // the frequency of this tree
    public HuffmanTree(int freq) { frequency = freq; }

    // compares on the frequency
    public int compareTo(HuffmanTree tree) {
        return frequency - tree.frequency;
    }
}

class HuffmanLeaf extends HuffmanTree {
    public final char value; // the character this leaf represents

    public HuffmanLeaf(int freq, char val) {
        super(freq);
        value = val;
    }
}

class HuffmanNode extends HuffmanTree {
    public final HuffmanTree left, right; // subtrees

    public HuffmanNode(HuffmanTree l, HuffmanTree r) {
        super(l.frequency + r.frequency);
        left = l;
        right = r;
    }
}

public class Huffman {
    // input is an array of frequencies, indexed by character code
    public static HuffmanTree buildTree(int[] charFreqs, char[] test2) {
        PriorityQueue<HuffmanTree> trees = new PriorityQueue<HuffmanTree>();
        // initially, we have a forest of leaves
        // one for each non-empty character
        for (int i = 0; i < charFreqs.length; i++)
            if (charFreqs[i] > 0)
                trees.offer(new HuffmanLeaf(charFreqs[i], test2[i]));

        assert trees.size() > 0;
        // loop until there is only one tree left
        while (trees.size() > 1) {
            // two trees with least frequency
            HuffmanTree a = trees.poll();
            HuffmanTree b = trees.poll();

            // put into new node and re-insert into queue
            trees.offer(new HuffmanNode(a, b));
        }
        return trees.poll();
    }

    public static void printCodes(HuffmanTree tree, StringBuffer prefix) {
        assert tree != null;
        if (tree instanceof HuffmanLeaf) {
            HuffmanLeaf leaf = (HuffmanLeaf)tree;

            // print out character, frequency, and code for this leaf (which is just the prefix)
            System.out.println(leaf.value + "\t" + leaf.frequency + "\t" + prefix);

        } else if (tree instanceof HuffmanNode) {
            HuffmanNode node = (HuffmanNode)tree;

            // traverse left
            prefix.append('0');
            printCodes(node.left, prefix);
            prefix.deleteCharAt(prefix.length()-1);

            // traverse right
            prefix.append('1');
            printCodes(node.right, prefix);
            prefix.deleteCharAt(prefix.length()-1);
        }
    }

    public static void main(String[] args) {
        //Symbols:
        String str = "12345678"; 
        char[] test2 = str.toCharArray();
        //Frequency (of the symbols above):
        int[] charFreqs = {36,18,12,9,7,6,5,4};


        // build tree
        HuffmanTree tree = buildTree(charFreqs,test2);

        // print out results
        System.out.println("SYMBOL\tFREQ\tHUFFMAN CODE");
        printCodes(tree, new StringBuffer());
    }
}

我得到的输出是:

SYMBOL  FREQ    HUFFMAN CODE
1           36          0
3           12          100
6           6           1010
5           7           1011
2           18          110
4           9           1110
8           4           11110
7           5           11111

太奇怪了,例如,符号7应该是:11110,符号8应该是:11111

你能帮我吗?

位模式的分配与代码的优化无关。 您的作业将正常进行。 没有什么奇怪的。 您可能还对2:110、3:100或4:1110、5:1011表示了担忧,但也可以。

在代码上施加顺序的唯一原因是减少了将代码从压缩器传输到解压缩器所需的位数。 除了发送代码之外,您还可以发送每个符号的代码长度,只要代码的长度和长度在两侧相同。

在那种情况下,方法通常是按数字顺序将代码分配给已排序的符号列表。 然后,如果分配的顺序是符号7,则它的代码“值”确实比符号8的值低。

对于您的示例,这样的规范代码将是:

1: 1 - 0
2: 3 - 100
3: 3 - 101
4: 4 - 1100
5: 4 - 1101
6: 4 - 1110
7: 5 - 11110
8: 5 - 11111

您只需简单地获取长度,并在相同的长度内对符号进行排序。 然后分配以0开头并递增的代码,随着长度的增加,在末尾添加位。

请注意,这是一个不寻常的示例,其中符号顺序也是频率顺序。 通常情况并非如此。

只要您再添加0即可了解完成位。 (3位以上的读数)

1 36 0 3 12 100 6 6 1010 5 7 1011'0 2 18 110 4 9 1110 8 4 11110 7 5 11111'0

要从答案的注释中回答问题:

嗨,马克,谢谢您的帮助,但我真的不明白您是如何获得这些代码的? 我需要在代码中做很多改动吗?

马克只是指霍夫曼编码的目标,即找到每个符号的最有效深度(位数),以便最小化所有符号(所有符号的频率[符号] * codeLenght [符号])的整体编码。

因此,实际上您要做的就是为每个符号确定叶子的深度(级别)。 现在,根据每个符号的深度对其进行排序,然后开始对它们进行计数。

现在,您只需向上计算模式。 就那么简单:

Example:
2x2: 00, 01  (next is 10)
4x3: 10 + (00, 01, 10) = 1000, 1001, 1010 (next is 1011)
5x3: 1011 + (0, 1, 0 + 10) = 10110, 10111, 10110 + 10 = 11000 (next would be 11001)...

最后一部分显示了如果元素数量大于两组之间可用的位差的情况。 它只是被添加到前缀。

这样,可以创建使用最少空间的霍夫曼代码。 由于这只是一棵树,因此您也可以从11111开始并删除1,然后得到在位数方面同样有效的另一代码系统。

可以添加的一件事是,进行了一些修改,将比较中的1(或0)的数量增加到0(或1),因此您还有另一次机会来压缩那些用于进一步压缩消息的位模式。


摘要:根据符号在频率树中的深度对符号进行排序。 通过加(减)一来构造代码。 下一个免费代码是下一组的开始前缀。 对于每个新成员,只需添加(减去)即可。 然后从代码中填充0(1)开始。

为了存储这样的树,请记住对相同的符号组使用相同的算法,您只需要存储以下信息:

组数n,n * {+ bitDepth,符号数i,s1,s2 ... si}。 由于存储n,+ bitDepth个符号本身就是压缩的主题,因此您可以使用可变位格式,甚至发出霍夫曼树,因为您会发现分布不均匀,这是看到压缩发生的主要原因与霍夫曼树。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM