C# 哈夫曼树:穿越

C# 哈夫曼树:穿越,c#,recursion,traversal,huffman-code,C#,Recursion,Traversal,Huffman Code,我不知道我将如何攻击哈夫曼树的遍历。这棵树是正确的,我只是很难想出如何以一种好的方式穿过它。由于某种原因,我的遍历方法没有给出任何结果 更新:清理了代码,使其更加面向对象 节点类: public class Node { public int frekvens; //Frequency public char tegn; //Symbol public Node venstre; //Left child public Node høyre; //Right ch

我不知道我将如何攻击哈夫曼树的遍历。这棵树是正确的,我只是很难想出如何以一种好的方式穿过它。由于某种原因,我的遍历方法没有给出任何结果

更新:清理了代码,使其更加面向对象

节点类:

public class Node
{
    public int frekvens; //Frequency
    public char tegn; //Symbol
    public Node venstre; //Left child
    public Node høyre; //Right child
    public string s; //result string
    public string resultat;
    public Node (char c) // Node constructor containing symbol.
    {
        frekvens = 1;
        tegn = c;
    }

    public Node (int f, Node venstre, Node høyre) // Node Constructor containing frequency and children
        {
            frekvens = f;
            this.venstre = venstre;
            this.høyre = høyre;
        }

    public Node (Node node) // Node constructor containing a node
        {
            frekvens = node.frekvens;
            tegn = node.tegn;
            this.venstre = venstre;
            this.høyre = høyre;
        }

    public void ØkMed1() // Inkrement frequency by one
    {
        frekvens = frekvens + 1;
    }


    public char getVenstreTegn ()
    {
        return venstre.tegn;
    }

    public char getHøyreTegn ()
    {
        return venstre.tegn;
    }

    public int getVenstreFrekvens ()
    {
        return venstre.frekvens;
    }

    public int getHøyreFrekvens ()
    {
        return høyre.frekvens;
    }

    public int getFrekvens()
    {
        return frekvens;
    }


    public bool ErTegn(char c)
    {
        if ( c == tegn)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    //Pretty sure this does not work as intended
    public string traverser (Node n) //Traverse the tree
    {
        if (n.tegn != '\0') //If the node containes a symbol --> a leaf
        {
            resultat += s;  
        }
        else
        {
            if (n.getVenstreTegn() == '\0') //If left child does not have a symbol
            {
                s += "0";
                traverser(n.venstre);
            }
            if (n.getHøyreTegn() == '\0') //If right child does not have a symbol
            {
                s += "1";
                traverser(n.høyre);
            }
        }
        return resultat;
    }
    public string Resultat() //Used priviously to check if i got the correct huffman tree
    {
        string resultat;
        resultat = "Tegn: " + Convert.ToString(tegn) +"  frekvens: " + Convert.ToString(frekvens) + "\n";
        return resultat;
    }
}
哈夫曼树类:

public class Huffman_Tre
{
    string treString;
    List<Node> noder = new List<Node>();
    public Node rot;
    public void bygg (string input)
    {
        bool funnet; //Found
        char karakter; //character

        for (int i = 0; i < input.Length;i++) //Loops through string and sets character
            //with coresponding freqeuncy in the node list
        {   
            karakter = input[i];
            funnet = false; //default
            for (int j = 0; j< noder.Count; j++)
            {
                if (noder[j].ErTegn(karakter) == false) //if the character already exists
                {
                    noder[j].ØkMed1(); //inkrement frequency by one
                    funnet = true; 
                    break;
                }
            }
            if (!funnet) //if the character does not exist 
            {
                noder.Add(new Node(karakter)); //add the character to list
            }
        }
        //Sorting node list acending by frequency
        var sortertListe = noder.OrderBy(c => c.frekvens).ToList();

        noder = sortertListe; 

        do
        {
            noder.Add(new Node((noder[0].frekvens + noder[1].frekvens), noder[0],noder[1]));

            //Remove the leaf nodes
            noder.RemoveAt(0);
            noder.RemoveAt(0); 
        } while(noder.Count >= 2);

    }

    public Node getRot()
    {
        return rot;
    }

    public string visTre()
    {

        foreach (Node node in noder)
        {
            treString += node.Resultat();
        }
        return treString;
    }
    public bool erNull()
    {
        if (noder[0].tegn == '\0')
        {
            return true;
        }
        else
            return false;
    }
}

由于我还有一点时间,我在玩C 6.0的时候制作了一个哈夫曼树的例子。它还没有优化,甚至到目前为止都没有!,但作为一个例子,它可以很好地工作。这将有助于你找到你的“挑战”可能出现的地方。由于我的英语远远好于我的斯堪的纳维亚知识,我用英语命名,希望你不要介意

首先,让我们从保持频率的类开始

public sealed class HuffmanFrequencyTable
{       
    #region Properties
    /// <summary>
    /// Holds the characters and their corresponding frequencies
    /// </summary>
    public Dictionary<char, int> FrequencyTable  { get; set; } = new Dictionary<char, int>();

    #endregion

    #region Methods

    /// <summary>
    /// Clears the internal frequency table
    /// </summary>
    public void Clear()
    {
        FrequencyTable?.Clear();            
    }

    /// <summary>
    /// Accepts and parses a new line (string) which is then 
    /// merged with the existing dictionary or frequency table
    /// </summary>
    /// <param name="line">The line to parse</param>
    public void Accept(string line)
    {
        if (!string.IsNullOrEmpty(line))
        {
            line.GroupBy(ch => ch).
                 ToDictionary(g => g.Key, g => g.Count()).
                 ToList().
                 ForEach(x => FrequencyTable[x.Key] = x.Value);
        }
    }

    /// <summary>
    /// Performs a dump of the frequency table, ordering all characters, lowest frequency first.
    /// </summary>
    /// <returns>The frequency table in the format 'character [frequency]'</returns>
    public override string ToString()
    {            
        return FrequencyTable?.PrintFrequencies();
    }
    #endregion
}
这种扩展方法可以方便地确定给定节点是否实际上是叶节点。叶子是一个没有子节点的节点,因此是二叉树的末端,或者更好的是该树的分支

现在有意思的是,我如何在这里工作。我已经构建了一个Windows窗体应用程序,它有3个文本框。一个用于实际输入,一个用于二进制编码输出,最后一个用于显示压缩结果。 我还放置了两个简单的按钮,一个用于执行哈夫曼编码,另一个用于哈夫曼解码

Huffman编码方法仅在encode按钮的eventhandler中编写如下:

 string input = tbInput.Text;
 Tree.BuildTree(input); //Build the huffman tree

 BitArray encoded = Tree.Encode(input); //Encode the tree

 //First show the generated binary output
 tbBinaryOutput.Text = string.Join(string.Empty, encoded.Cast<bool>().Select(bit => bit ? "1" : "0"));

  //Next, convert the binary output to the new characterized output string.       
  byte[] bytes = new byte[(encoded.Length / 8) + 1];
  encoded.CopyTo(bytes, 0);

  tbOutput.Text = Encoding.Default.GetString(bytes); //Write the compressed output to the textbox.
请注意,编码的二进制字符串没有任何尾随位。我将把它留给C的编码机制。这的缺点是,我必须在解码时跟踪它

解码现在也不太难。虽然,在这个例子中,我使用了上面的编码代码生成的压缩输出。另外,我假设哈夫曼树和它的频率表!!!都已经建成了。通常,频率表存储在压缩文件中,以便可以重建

 //First convert the compressed output to a bit array again again and skip trailing bits.            
 bool[] boolAr = new BitArray(Encoding.Default.GetBytes(tbOutput.Text)).Cast<bool>().Take(Tree.BitCountForTree).ToArray();
 BitArray encoded = new BitArray( boolAr );

 string decoded = Tree.Decode(encoded);
 MessageBox.Show(decoded, "Decoded result: ", MessageBoxButtons.OK, MessageBoxIcon.Information);
请注意我创建的恶意攻击,因为Encoding.Default.GetBytestbOutput.Text肯定会生成一个字节数组,它可能包含不需要解码的尾随位。因此,基于重建树,我只获取实际需要的比特数

因此,在运行时,我的示例提供了以下输出,当使用“世界著名句子”时,敏捷的棕色狐狸跳过了懒惰的程序员:

按下Huff编码按钮后:

 string input = tbInput.Text;
 Tree.BuildTree(input); //Build the huffman tree

 BitArray encoded = Tree.Encode(input); //Encode the tree

 //First show the generated binary output
 tbBinaryOutput.Text = string.Join(string.Empty, encoded.Cast<bool>().Select(bit => bit ? "1" : "0"));

  //Next, convert the binary output to the new characterized output string.       
  byte[] bytes = new byte[(encoded.Length / 8) + 1];
  encoded.CopyTo(bytes, 0);

  tbOutput.Text = Encoding.Default.GetString(bytes); //Write the compressed output to the textbox.
按下Huff解码按钮后:

 string input = tbInput.Text;
 Tree.BuildTree(input); //Build the huffman tree

 BitArray encoded = Tree.Encode(input); //Encode the tree

 //First show the generated binary output
 tbBinaryOutput.Text = string.Join(string.Empty, encoded.Cast<bool>().Select(bit => bit ? "1" : "0"));

  //Next, convert the binary output to the new characterized output string.       
  byte[] bytes = new byte[(encoded.Length / 8) + 1];
  encoded.CopyTo(bytes, 0);

  tbOutput.Text = Encoding.Default.GetString(bytes); //Write the compressed output to the textbox.

<>现在这个代码可以使用一些优化,因为你可以考虑使用数组而不是字典。还有更多,但这取决于您的考虑。

因为我还有一点时间,所以在玩C 6.0时,我制作了一个哈夫曼树的示例。它还没有优化,甚至到目前为止都没有!,但作为一个例子,它可以很好地工作。这将有助于你找到你的“挑战”可能出现的地方。由于我的英语远远好于我的斯堪的纳维亚知识,我用英语命名,希望你不要介意

首先,让我们从保持频率的类开始

public sealed class HuffmanFrequencyTable
{       
    #region Properties
    /// <summary>
    /// Holds the characters and their corresponding frequencies
    /// </summary>
    public Dictionary<char, int> FrequencyTable  { get; set; } = new Dictionary<char, int>();

    #endregion

    #region Methods

    /// <summary>
    /// Clears the internal frequency table
    /// </summary>
    public void Clear()
    {
        FrequencyTable?.Clear();            
    }

    /// <summary>
    /// Accepts and parses a new line (string) which is then 
    /// merged with the existing dictionary or frequency table
    /// </summary>
    /// <param name="line">The line to parse</param>
    public void Accept(string line)
    {
        if (!string.IsNullOrEmpty(line))
        {
            line.GroupBy(ch => ch).
                 ToDictionary(g => g.Key, g => g.Count()).
                 ToList().
                 ForEach(x => FrequencyTable[x.Key] = x.Value);
        }
    }

    /// <summary>
    /// Performs a dump of the frequency table, ordering all characters, lowest frequency first.
    /// </summary>
    /// <returns>The frequency table in the format 'character [frequency]'</returns>
    public override string ToString()
    {            
        return FrequencyTable?.PrintFrequencies();
    }
    #endregion
}
这种扩展方法可以方便地确定给定节点是否实际上是叶节点。叶子是一个没有子节点的节点,因此是二叉树的末端,或者更好的是该树的分支

现在有意思的是,我如何在这里工作。我已经构建了一个Windows窗体应用程序,它有3个文本框。一个用于实际输入,一个用于二进制编码输出,最后一个用于显示压缩结果。 我还放置了两个简单的按钮,一个用于执行哈夫曼编码,另一个用于哈夫曼解码

Huffman编码方法仅在encode按钮的eventhandler中编写如下:

 string input = tbInput.Text;
 Tree.BuildTree(input); //Build the huffman tree

 BitArray encoded = Tree.Encode(input); //Encode the tree

 //First show the generated binary output
 tbBinaryOutput.Text = string.Join(string.Empty, encoded.Cast<bool>().Select(bit => bit ? "1" : "0"));

  //Next, convert the binary output to the new characterized output string.       
  byte[] bytes = new byte[(encoded.Length / 8) + 1];
  encoded.CopyTo(bytes, 0);

  tbOutput.Text = Encoding.Default.GetString(bytes); //Write the compressed output to the textbox.
请注意,编码的二进制字符串没有任何尾随位。我将把它留给C的编码机制。这的缺点是,我必须在解码时跟踪它

解码现在也不太难。虽然,在这个例子中,我使用了上面的编码代码生成的压缩输出。另外,我假设哈夫曼树和它的频率表!!!都已经建成了。通常,频率表存储在压缩文件中,以便可以重建

 //First convert the compressed output to a bit array again again and skip trailing bits.            
 bool[] boolAr = new BitArray(Encoding.Default.GetBytes(tbOutput.Text)).Cast<bool>().Take(Tree.BitCountForTree).ToArray();
 BitArray encoded = new BitArray( boolAr );

 string decoded = Tree.Decode(encoded);
 MessageBox.Show(decoded, "Decoded result: ", MessageBoxButtons.OK, MessageBoxIcon.Information);
请注意我创建的恶意攻击,因为Encoding.Default.GetBytestbOutput.Text肯定会生成一个字节数组,它可能包含不需要解码的尾随位。因此,基于重建树,我只获取实际需要的比特数

因此,在运行时,我的示例提供了以下输出,当使用“世界著名句子”时,敏捷的棕色狐狸跳过了懒惰的程序员:

按下Huff编码按钮后:

 string input = tbInput.Text;
 Tree.BuildTree(input); //Build the huffman tree

 BitArray encoded = Tree.Encode(input); //Encode the tree

 //First show the generated binary output
 tbBinaryOutput.Text = string.Join(string.Empty, encoded.Cast<bool>().Select(bit => bit ? "1" : "0"));

  //Next, convert the binary output to the new characterized output string.       
  byte[] bytes = new byte[(encoded.Length / 8) + 1];
  encoded.CopyTo(bytes, 0);

  tbOutput.Text = Encoding.Default.GetString(bytes); //Write the compressed output to the textbox.
按下Huff解码按钮后:

 string input = tbInput.Text;
 Tree.BuildTree(input); //Build the huffman tree

 BitArray encoded = Tree.Encode(input); //Encode the tree

 //First show the generated binary output
 tbBinaryOutput.Text = string.Join(string.Empty, encoded.Cast<bool>().Select(bit => bit ? "1" : "0"));

  //Next, convert the binary output to the new characterized output string.       
  byte[] bytes = new byte[(encoded.Length / 8) + 1];
  encoded.CopyTo(bytes, 0);

  tbOutput.Text = Encoding.Default.GetString(bytes); //Write the compressed output to the textbox.
<>现在这个代码可以使用一些优化,因为你可以考虑使用数组而不是字典。
还有更多,但这取决于您的考虑。

Sindre,也许首先使用二叉树更方便。正如哈夫曼的算法将根据字符的频率或形成根的频率总和来确定二进制数字1或0。如果你还需要更多的提示,我忽略了你已经在使用二叉树的事实。我道歉。尽管如此,它还是促使我建立了一个带有遍历的哈夫曼算法。当然,我已经在我的答案中发布了它。顺便说一下,您的遍历不起作用,因为您实际上没有遍历树的左右节点。感谢您的回复,在我昨天收到gmail之前,我以为这个帖子已经死了:我写的代码写得太快了。在高中学习c一个月后,当我现在看到它的时候,它不是很好。我将很快重新制作这个应用程序,使它完全工作。这个哈夫曼编码的算法非常简洁,也许首先使用二叉树会更方便。正如哈夫曼的算法将根据字符的频率或形成根的频率总和来确定二进制数字1或0。如果你还需要更多的提示,我忽略了你已经在使用二叉树的事实。我道歉。尽管如此,它还是促使我建立了一个带有遍历的哈夫曼算法。当然,我已经在我的答案中发布了它。顺便说一下,您的遍历不起作用,因为您实际上没有遍历树的左右节点。感谢您的回复,在我昨天收到gmail之前,我以为这个帖子已经死了:我写的代码写得太快了。在高中学习c一个月后,当我现在看到它的时候,它不是很好。我将很快重新制作这个应用程序,使它完全工作。这是一个相当简洁的算法,这个哈夫曼代码回答得很好!随着我编程语言知识的提高,我越来越尊重好的代码。我将尽量不看你的代码太多,而是阅读你的帮助说明,因为我确实希望从头开始创建这个应用程序。现在应该容易多了:D这是一个非常有趣的程序,我希望你在写的时候也能有一些乐趣;我真的做到了,别担心:-。我很高兴它有帮助!回答得好!随着我编程语言知识的提高,我越来越尊重好的代码。我将尽量不看你的代码太多,而是阅读你的帮助说明,因为我确实希望从头开始创建这个应用程序。现在应该容易多了:D这是一个非常有趣的程序,我希望你在写的时候也能有一些乐趣;我真的做到了,别担心:-。我很高兴它有帮助!