Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何解析树的一行字符串表示?_C#_.net_Algorithm_Data Structures_Tree - Fatal编程技术网

C# 如何解析树的一行字符串表示?

C# 如何解析树的一行字符串表示?,c#,.net,algorithm,data-structures,tree,C#,.net,Algorithm,Data Structures,Tree,我得到了树的一行字符串表示。树中的键是int。关键点是唯一的(但稀疏)。例如字符串 2[5],3[6,12[15,16]] 描述树 2 \-- 5 3 |-- 6 `-- 12 |-- 15 `-- 16 我想用GetChildren方法将单行字符串解析为ChildLookup类,以便 GetChildren(2)返回[5] GetChildren(5)返回[] GetChildren(3)返回[6,12] GetChildren(10)抛出KeyNotFoundExcep

我得到了树的一行字符串表示。树中的键是int。关键点是唯一的(但稀疏)。例如字符串

2[5],3[6,12[15,16]]
描述树

2
\-- 5
3
|-- 6
`-- 12
    |-- 15
    `-- 16
我想用GetChildren方法将单行字符串解析为ChildLookup类,以便

  • GetChildren(2)返回[5]
  • GetChildren(5)返回[]
  • GetChildren(3)返回[6,12]
  • GetChildren(10)抛出KeyNotFoundException
我该怎么做

动机:我试图用相同的API对为不同类编写的算法进行单元测试。

类似于:

  • 扫描树以查找父密钥。如果找不到,则返回null
  • 从下一个打开的括号中,您将添加到子输出列表中
  • 从这里开始跟踪打开/关闭括号,作为树的深度
  • 仅当您的级别低于父级时才添加到您的退货列表中

  • 当然,您可以构建整个树,但这可能比您需要做的更多。

    这里有一个ersatz LR lexer/解析器(在Python中,但移植应该很容易)。假设输入有效

    #!/usr/bin/env python3
    import re
    
    
    def parse(s):
        children = {}
        stack = []
        for token in re.findall('[+-]?[0-9]+|.', s):
            if token == '[':
                stack.append(n)
            elif token == ']':
                del stack[-1]
            elif token != ',':
                n = int(token)
                children[n] = []
                if stack:
                    children[stack[-1]].append(n)
        return children
    
    
    >>> print(parse('2[5],3[6,12[15,16]]'))
    {16: [], 2: [5], 3: [6, 12], 5: [], 6: [], 12: [15, 16], 15: []}
    

    您可以编写一个状态机解析器来构建树

    开始声明一个
    节点
    类,如下所示:

    public class Node
    {
        public Node Parent;
        public string Value;
        public List<Node> SubNodes;
    }
    
    Node root = new Node { Parent = null, Value = string.Empty, SubNodes = new List<Node>() };
    Node node = root;
    
    foreach (char c in tree)
    {
        switch (c)
        {
            case '[':        // Start a new node, child of the current node 
                node = new Node { Parent = node, Value = string.Empty, SubNodes = new List<Node>() };
                node.Parent.SubNodes.Add(node);
                break;
            case ',':        // Start a new node, but at the same level of the current node
                node = new Node { Parent = node.Parent, Value = string.Empty, SubNodes = new List<Node>() };
                node.Parent.SubNodes.Add(node);
                break;
            case ']':        // Back to parent of the current node
                node = node.Parent;
                break;
            default:         // Add char to current node value
                node.Value += c;
                break;
        }
    }
    
    root
     |--2
     |  |--5
     |
     |--3
        |--6
        |--12
           |--15
           |--16
    
    解析器应该是这样的:

    public class Node
    {
        public Node Parent;
        public string Value;
        public List<Node> SubNodes;
    }
    
    Node root = new Node { Parent = null, Value = string.Empty, SubNodes = new List<Node>() };
    Node node = root;
    
    foreach (char c in tree)
    {
        switch (c)
        {
            case '[':        // Start a new node, child of the current node 
                node = new Node { Parent = node, Value = string.Empty, SubNodes = new List<Node>() };
                node.Parent.SubNodes.Add(node);
                break;
            case ',':        // Start a new node, but at the same level of the current node
                node = new Node { Parent = node.Parent, Value = string.Empty, SubNodes = new List<Node>() };
                node.Parent.SubNodes.Add(node);
                break;
            case ']':        // Back to parent of the current node
                node = node.Parent;
                break;
            default:         // Add char to current node value
                node.Value += c;
                break;
        }
    }
    
    root
     |--2
     |  |--5
     |
     |--3
        |--6
        |--12
           |--15
           |--16
    
    最后,您的
    ChildLookup
    如下所示:

    var lookup = new ChildLookup(root);
    ...
    
    class ChildLookup
    {
        Dictionary<string, Node> nodes;
    
        public ChildLookup(Node node)
        {
            nodes = new Dictionary<string, Node>();
            AddNodes(node);
        }
    
        private void AddNodes(Node node)
        {
            nodes[node.Value] = node;
            foreach (var item in node.SubNodes)
                AddNodes(item);
        }
    
        public IEnumerable<string> GetChildren(string key)
        {
            if (!nodes.ContainsKey(key)) throw new KeyNotFoundException();
    
            return nodes[key].SubNodes.Select(c => c.Value);
        }
    }
    
    var lookup=新的子查找(根);
    ...
    类子查找
    {
    字典节点;
    公共子查找(节点)
    {
    节点=新字典();
    添加节点(node);
    }
    私有void AddNodes(节点节点)
    {
    节点[node.Value]=节点;
    foreach(节点子节点中的变量项)
    添加节点(项目);
    }
    公共IEnumerable GetChildren(字符串键)
    {
    如果(!nodes.ContainsKey(key))抛出新的KeyNotFoundException();
    返回节点[key]。子节点。选择(c=>c.Value);
    }
    }
    
    我打赌这是Haskell程序员可以在一行中完成的事情。因此,您不想解析整个树,而实际上只查找特定节点的直接后代?在这种情况下,哈希表(映射节点=>list of children)的性能(如果有很多查找)将比实际的树更高。