2

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

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 树,如果它更大则向右移动,如果它更小则向左移动。

但是因为节点不使用整数双精度等,而仅使用包含字符作为字符串或 null 的对象来表示它不是叶子,而只是根。另一种选择是按顺序运行以找到我正在寻找的叶子,但同时我将如何确定我是向右走这么多次还是离开这么多次来获得角色。

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。

让我感到困惑的是,您无法确定任何东西在哪里,因为树显然不平衡,并且无法确定角色是在您所在位置的右侧还是左侧。所以你必须搜索整棵树,但如果你找到一个不是你要找的节点,你必须回溯到另一个根才能找到其他叶子。

4

5 回答 5

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”的字符串,你会做 search(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);

}
于 2010-07-22T17:13:35.297 回答
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]

老实说,我没有查看您的代码。如何处理这棵花哨的树应该很明显了。

于 2010-07-22T17:25:46.143 回答
0

这是一个 Scala 实现:http ://blog.flotsam.nl/2011/10/huffman-coding-done-in-scala.html

于 2012-01-14T16:40:58.767 回答
0

当我尝试 Huffman 编码编码树时,我考虑了两个选项。

选项1:使用基于指针的二叉树。我编写了大部分代码,然后觉得,要从叶子跟踪树以找到编码,我需要父指针。否则,就像这篇文章中提到的那样,您搜索树并不是立即找到编码的解决方案。基于指针的树的缺点是,我认为树中的每个节点都必须有 3 个指针,这太多了。遵循指针的代码很简单,但比选项 2 更复杂。

选项 2:使用基于数组的树来表示您将在运行中用于编码和解码的编码树。所以如果你想要一个字符的编码,你可以在数组中找到这个字符。很简单,我使用了一张非常合适的桌子,然后我就得到了叶子。现在我追踪到数组中索引 1 处的根。我为父母做了一个 (current_index / 2) 。如果子索引是父/2,它是左,否则是右。

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

此外,您在解码 Huffman 代码时不搜索 (root, 10110)。您只需通过编码比特流的流遍历树,根据您的比特向左或向右,当您到达叶子时,您输出字符。

希望这会有所帮助。

Harisankar Krishna Swamy(示例

于 2011-12-11T05:37:36.293 回答
0

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

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

不久前,我有一个任务要编写一个像你一样的霍夫曼编码器/解码器,我写了一篇博客文章,其中包含 Java 代码和更长的解释:http ://www.byteauthor.com/2010/09/huffman-coding -java中/

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

于 2012-01-11T22:22:49.070 回答