【问题标题】:Recursively creating a mathematical expression binary tree递归创建数学表达式二叉树
【发布时间】:2011-07-23 21:09:44
【问题描述】:

下午好,

我已经用 C# 实现了一个简单的二叉树,我打算用它来递归地创建数学表达式树。但是我遇到了问题,因为自从我不得不进行递归调用以来已经有好几年了,我一直在努力了解为什么以下内容仅适用于深度为 2 的二叉树而不适用于任何更深的树。

当然,如果递归是正确的,它应该能够构建深度为 n 的树。这是代码:

Node<T> f;
Node<T> t;
Node<T> subRoot;
Node<T> root;

////Only works with trees of depth 2.

private Node<T> Tree(List<T> prefixBank, int maxDepth)
{
    if (prefixBank.Count != 0)
    {
        if (maxDepth == 0)
        {
            t = new Node<T>(prefixBank[0]);
            subRoot.Add(t);

            prefixBank.Remove(prefixBank[0]);
        }
        else
        {
            if (root == null)
            {
                root = new Node<T>(prefixBank[0]);
                prefixBank.Remove(prefixBank[0]);
                Tree(prefixBank, maxDepth - 1);
            }

            f = new Node<T>(prefixBank[0]);

            if (isOperator(f))
            {
                root.Add(f);
                prefixBank.Remove(prefixBank[0]);
                subRoot = f;
            }

            for (int i = 0; i < 2; i++)
            {
                Tree(prefixBank, maxDepth - 1);
            }
        }
    }
return f;
}

上述函数采用构成前缀数学表达式的字符列表(例如* + 3 4 - 5 6)。令人讨厌的是,我使用以下代码递归地创建前缀表达式:

string prefixExpression = String.Empty;

private string generateExpression(Random rand, GenerationMethod generationMethod, int maxDepth)
{
    if ((maxDepth == 0) || ((generationMethod == GenerationMethod.Grow) && (rand.NextDouble() < randomRate)))
    {
        operand.GenerateValue(Type.Terminal, rand);
        prefixExpression += " " + operand.Value;
    }
    else
    {
        operator.GenerateValue(Type.Function, rand);
        prefixExpression += " " + operator.GeneValue;

        //2 is the arity of the function right an arity checker function so that unary operators can be utilised
        for (int i = 0; i < 2; i++)
        {
            generateExpression(rand, generationMethod, maxDepth - 1);
        };
    }
    return prefixExpression;
}

我尝试以与生成字符串相同的方式创建树,但无法使其以任何常识方式工作。我正在使用binary tree class found on MSDN 的略微修改版本。我对其进行了修改,使其具有自动添加到左子树或右子树的添加功能,因此我不必像此示例那样指定 Root.Left.Left.Left 等来创建树。

如果任何人可以帮助我更正我的递归树创建代码,使其适用于 n 深度的树,我将不胜感激。我对 C# 比较陌生,所以如果上面的内容有点粗略,我深表歉意。

【问题讨论】:

  • 与您的问题无关,但不要像您那样连接字符串,您应该使用StringBuilder
  • 另外,使用List&lt;T&gt;这种方式效率非常低。

标签: c# parsing data-structures recursion


【解决方案1】:

在进行这样的递归调用时,您不应该依赖全局变量(并且通常,变量的范围应该尽可能窄,f 和 @987654322 似乎没有任何理由@ 是类级别的变量)。您的代码对我来说没有多大意义,但我想主要问题是您将每个节点直接添加到根目录。我会这样做:

public Node<T> Tree(Queue<T> prefixBank)
{
    var node = new Node<T>(prefixBank.Dequeue());

    if (isOperator(node))
    {
        for (int i = 0; i < 2; i++)
        {
            node.Add(Tree(prefixBank));
        }
    }

    return node;
}

我认为没有太多理由在那里拥有maxDepth,但如果你真的想要它,它应该很容易实现。

此外,拥有节点的继承层次结构(如 NumberNodeOperatorNode)可能是有意义的,但这取决于您究竟想对它们做什么。

编辑:@Eric,不多。我可以使用IEnumerator&lt;T&gt; 而不是Queue&lt;T&gt;(现在我想,这可能是一个更好的选择)。而且我必须将结果的创建推迟到最后,这也意味着我必须修改isOperator() 才能处理T 而不是Node&lt;T&gt;

public Node<T> Tree(IEnumerable<T> prefixBank)
{
    return Tree(prefixBank.GetEnumerator());
}

public Node<T> Tree(IEnumerator<T> enumerator)
{
    enumerator.MoveNext();
    var value = enumerator.Current;

    var children = new List<Node<T>>(2);

    if (isOperator(value))
    {
        for (int i = 0; i < 2; i++)
        {
            children.Add(Tree(enumerator));
        }
    }

    return new Node<T>(value, children);
}

【讨论】:

  • 一个挑战,如果你有一些空闲时间:假设(1)你不希望通过出队来破坏输入对象,并且(2)节点是不可变的。你会如何修改你的算法?
  • 这很奏效。在这里学到了宝贵的一课,谢谢@svick。
  • 您认为同时生成节点并将其添加到树中会更好。与将它们存储在队列中的中间阶段相反。我假设这也可以递归完成???
  • 是的,我认为没有额外的步骤会更好。 Node&lt;T&gt; generate(int level) { return level == 0 ? new Node&lt;T&gt;(generateOperand(), new Node&lt;T&gt;[0]) : new Node&lt;T&gt;(generateOperator(), new[] { generate(level-1), generate(level-1) }; }
猜你喜欢
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 2018-03-11
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
  • 2020-08-20
相关资源
最近更新 更多