霍夫曼树编码

时间:2010-07-22 17:08:26

标签: java traversal huffman-code

我之前问过的我的霍夫曼树有另一个问题!这是代码:

package huffman;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Huffman {

    public ArrayList<Frequency> fileReader(String file)
    {
        ArrayList<Frequency> al = new ArrayList<Frequency>();
        Scanner s;
        try {

            s = new Scanner(new FileReader(file)).useDelimiter("");
            while (s.hasNext())
            {
                boolean found = false;
                int i = 0;
                String temp = s.next();
                while(!found)
                {


                    if(al.size() == i && !found)
                    {
                        found = true;
                        al.add(new Frequency(temp, 1));
                    }
                    else if(temp.equals(al.get(i).getString()))
                    {
                        int tempNum = al.get(i).getFreq() + 1;
                        al.get(i).setFreq(tempNum);
                        found = true;
                    }
                    i++;

                }



            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return al;
    }
    public Frequency buildTree(ArrayList<Frequency> al)
    {
        Frequency r = al.get(1);
        PriorityQueue<Frequency> pq = new PriorityQueue<Frequency>();
        for(int i = 0; i < al.size(); i++)
        {
            pq.add(al.get(i));          
        }
        /*while(pq.size() > 0)
        {
            System.out.println(pq.remove().getString());
        }*/

        for(int i = 0; i < al.size() - 1; i++)
        {   
           Frequency p = pq.remove(); 
           Frequency q = pq.remove();
           int temp = p.getFreq() + q.getFreq();
           r = new Frequency(null, temp);
           r.left = p; 
           r.right = q;
           pq.add(r);  // put in the correct place in the priority queue
        }
        pq.remove(); // leave the priority queue empty
        return(r); // this is the root of the tree built

    }

    public void inOrder(Frequency top)
    {
        if(top == null)
        {
            return;
        }
        else
        {
            inOrder(top.left);
            System.out.print(top.getString() +", ");
            inOrder(top.right);
            return;
        }
    }
    public void printFreq(ArrayList<Frequency> al)
    {
        for(int i = 0; i < al.size(); i++)
        {
            System.out.println(al.get(i).getString() + "; " + al.get(i).getFreq());
        }
    }

}

现在需要做的是我需要创建一个方法来搜索树,找到特定字符的二进制代码(011001等)。做这个的最好方式是什么?我想也许我会通过树进行正常搜索,就好像它是一个AVL树,如果它更大或向左,如果它更小。

但是因为节点不使用ints double等,而只使用包含字符作为字符串的对象或null来表示它不是叶子而是仅仅是根。另一种选择是按顺序运行以查找我正在寻找的叶子但同时如何确定我是否正确地进行了这么多次或者多次离开以获得角色。 / p>

package huffman;

public class Frequency implements Comparable  {
    private String s;
    private int n;
    public Frequency left;
    public Frequency right;

    Frequency(String s, int n)
    {
        this.s = s;
        this.n = n;
    }
    public String getString()
    {
        return s;
    }
    public int getFreq()
    {
        return n;
    }
    public void setFreq(int n)
    {
        this.n = n;
    }
    @Override
    public int compareTo(Object arg0) {
        Frequency other = (Frequency)arg0;

        return n < other.n ? -1 : (n == other.n ? 0 : 1);
    }
}

我要做的是找到实际到达每个角色的二进制代码。因此,如果我尝试编码aabbbcccc,我将如何创建一个字符串,其中包含左侧的二进制代码为0,右侧为1。

让我感到困惑的是因为你无法确定任何地方,因为树显然是不平衡的,并且无法确定角色是否正确或离开你的位置。因此,您必须搜索整个树,但如果您到达的节点不是您正在寻找的节点,那么您可以回溯到另一个根目录以获取其他树叶。

5 个答案:

答案 0 :(得分:1)

请记住,如果您有1001,那么您将永远不会有10010或10011.所以您的基本方法看起来像这样(在伪代码中):

if(input == thisNode.key) return thisNode.value
if(input.endsWith(1)) return search(thisNode.left)
else return search(thisNode.right)

我没有阅读你的程序来弄清楚如何整合它,但这简直就是霍夫曼编码的关键要素

尝试这样的事情 - 你正试图寻找令牌。所以如果你想找到“10010”的字符串,你就要搜索(root,“10010”)

String search(Frequency top, String token) {
    return search(top,token,0);
}

// depending on your tree, you may have to switch top.left and top.right
String search(Frequency top, String token, int depth) {
    if(token.length() == depth) return "NOT FOUND";
    if(token.length() == depth - 1) return top.getString();
    if(token.charAt(depth) == '0') return search(top.left,token,depth+1);
    else return search(top.right,token,depth+1);

}

答案 1 :(得分:1)

遍历霍夫曼树节点以获得类似{'a':“1001”,“b”:“10001”}等的地图。您可以使用此地图将二进制代码转换为特定字符。

如果您需要反向操作,只需将其作为状态机处理:

state = huffman_root
for each bit
    if (state.type == 'leaf')
        output(state.data);
        state = huffman_root
    state = state.leaves[bit]

老实说,我没有查看你的代码。如何处理奇特的树应该很明显。

答案 2 :(得分:0)

当我开始使用霍夫曼编码编码树时,我考虑了两个选项。

选项1:使用基于指针的二叉树。我对其中的大部分进行了编码然后感觉到,为了从叶子中追踪树来找到编码,我需要父指针。另外,就像在这篇文章中提到的那样,你搜索树,这不是立即找到编码的解决方案。基于指针的树的缺点是,我必须为树中的每个节点提供3个指针,我认为这个指针太多了。遵循指针的代码很简单,但在选项2中更复杂。

选项2:使用基于数组的树来表示将在运行中用于编码和解码的编码树。因此,如果您想要对字符进行编码,则可以在数组中找到该字符。非常直接,我使用一张桌子,所以在那里,我得到了叶子。现在我追踪到数组中索引1的根。我为父母做了一个(current_index / 2)。如果子索引是父/ 2,则它是左边的,否则是右边的。

选项2很容易编码,尽管数组可以有一个空格。我认为它在性能上比基于指针的树更好。除了识别根和叶子之外,现在是指数而不是对象类型的问题。 ;)如果你必须发送你的树,这也将是非常有用的!?

另外,在解码霍夫曼代码时,你不要搜索(root,10110)。您只需在编码的比特流中走过树,根据您的位向左或向右移动,当您到达叶子时,输出字符。

希望这有用。

Harisankar Krishna Swamy(example

答案 3 :(得分:0)

我想你的作业要么已经完成,要么已经很晚了,但也许这会对其他人有帮助。

实际上非常简单。您创建一个树,其中0向右,1向左。读取流将导航您浏览树。当你打了一片叶子,你找到了一封信并从头开始。就像glowcoder说的那样,你永远不会在非叶节点上有一个字母。该树还涵盖了每个可能的位序列。因此,无论编码输入如何,以这种方式导航始终有效。

我有一个编写霍夫曼编码器/解码器的任务,就像你刚才一样,我写了一篇博客文章,里面有Java代码和更长的解释:http://www.byteauthor.com/2010/09/huffman-coding-in-java/

PS。大多数解释是使用尽可能少的位数来序列化霍夫曼树,但编码/解码算法在源代码中非常简单。

答案 4 :(得分:0)