树结构为字符串 - 如何匹配嵌套大括号?

时间:2011-07-07 04:27:00

标签: c# algorithm parsing tree

我设置了一个树结构,并希望将其保存到字符串中,并使用最少量的文本将其读取(因此XML序列化已经完成)。我为此设置了一个简单的(或者我认为)结构,但无法弄清楚如何读取它,所以我的结构很可能必须改变。让我举一个例子。

我的树由X,Y坐标组成,如下例所示:

[a,b]
  |-----|
[c,d] [e,f]
        |-----|-----|
      [g,h] [i,j] [k,l]

当我运行算法将此树转换为字符串时,我得到以下输出:

a,b(c,d()e,f(g,h()i,j()k,l()))

这里是我正在使用的代码:

public string SerializeMe()
{
    StringBuilder ret = new StringBuilder(this.Value.ToString())
    ret.Append("(");
    foreach (SimpleTreeNode<T> child in _Children)
    {
        ret.Append(child.SerializeMe());
    }
    ret.Append(")");
    return ret.ToString();
}

这很好用,但现在我无法将字符串解析回我的树结构中。我可以将子字符串转到第一个打开的大括号并将其转换为节点的值,但我不确定如何将其余字符串拆分为子节点。有什么方法可以轻松找到一个开口支架,然后找到它的闭合支架?我已经研究了一些复杂的正则表达式的东西,我无法正常工作,很快就完全迷失了。

有没有人有任何想法?

编辑:
这是我到目前为止的代码:

public static SimpleTreeNode<SPoint> ParsePointTree(string input)
{
    //if the input string is empty, there is no node here. Return null.
    if (string.IsNullOrEmpty(input)) return null;
    else
    {
        //get the value from the first part of the string
        string valString = input.Substring(0, input.IndexOf('('));
        SPoint value = (SPoint)valString;
        SimpleTreeNode<SPoint> node = new SimpleTreeNode<SPoint>(value);

        //now we have the child nodes enclosed in brackets
        string innerstring = input.Substring(input.IndexOf('('));

        List<string> children = new List<string>();
        // how do we split innerstring into siblings?? //

        foreach (string child in children)
        {
            node.Children.Add(SimpleTreeNode<SPoint>.ParsePointTree(child));
        }

        return node;
    }
}

我遇到的问题是,我会得到一个必须分成兄弟姐妹的字符串。在上面的示例中,c,de,f是兄弟姐妹,以(c,d()e,f(g,h()i,j()k,l()))的形式表示。我需要将此字符串拆分为c,d()e,f(g,h()i,j()k,l()),这就是我被困住的地方。

2 个答案:

答案 0 :(得分:3)

您可以使用堆栈和2个局部变量来解析类似的字符串。如果使用广度优先遍历而不是深度优先序列化树,则不需要堆栈(顺便说一下,在任何一种情况下都不必递归)。

递归解决方案只是利用调用堆栈,可能导致堆栈溢出 - see here for a better explanation为什么这不是最佳方式。

foreach (char c in "a(c()e(g()i()k()))")
{
    if (c == '(')
    {
        Stack.Push(Parent);
        Parent = Child;
    }
    else if (c == ')')
    {
        Child = Parent;
        Parent = Stack.Pop();
    }
    else
    {
        Child = new SimpleTreeNode() { Value = c };
        Parent.Children.Add(Child);
    }
}

答案 1 :(得分:1)

像这样(伪代码):

function parse() =
    label = read_until('(',')');
    read_char('(')
    children = []
    while not peek_char(')') do
        child = parse()
        children.add(child)
    read_char(')')
    return new Node(label,children)
  • read_until(...)读取,但不包括给定的字符。
  • read_char(c)读取一个字符,如果不匹配则会引发错误。
  • peek_char(c)查看下一个字符,并返回一个指示是否匹配的真值。