【问题标题】:defining multi branching nested tree in python recursively在python中递归定义多分支嵌套树
【发布时间】:2015-11-28 10:48:07
【问题描述】:

我正在尝试在 Python 中实现一些基本的递归结构,但没有取得很大成功。我有一棵以嵌套列表形式表示的树,如下所示:

ex = ['A',
      ['A1',
       ['A11', 'tag'],
       ['A12', 'tag'],
       ['A13',
        ['A131', 'tag'],
        ['A132',
         ['A1321', 'tag'],
         ['A1322', 'tag']]]],
      ['A2', 'tag'],
      ['A3',
       ['A31',
        ['A311', 'tag'],
        ['A312', 'tag']],
       ['A32', 'tag'],
       ['A33',
        ['A331',
         ['A3311', 'tag'],
         ['A3312', 'tag']]],
       ['A34', 'tag'],
       ['A35',
        ['A351', 'tag'],
        ['A352',
         ['A3521', 'tag'],
         ['A3522', 'tag']]]],
      ['A4', 'tag']]

我已经定义了一个 Node 类,它允许指定标签 'A', 'A1', ... 并添加子级。通过注意到children 不是一个列表,可以检索终端节点。

class Node(object):
    def __init__(self, tag, parent=None, children=[]):
        self.tag = tag
        self.parent = parent
        self.children = children
        self.is_terminal = False if isinstance(children, list) else True

    def add_child(self, node):
        if not isinstance(node, Node):
            raise ValueError("Cannot append node of type: [%s]" % type(node))
        if self.is_terminal:
            raise ValueError("Cannot append node to terminal")
        else:
            self.children.append(node)

现在我无法实现一个函数,该函数会将基于列表的树递归地转换为基于节点的树:

tree = Node(tag='A', 
            children=[Node(tag='A1',
                           children=[Node(tag='A11',
                                          children='tag'),
                                     Node(tag='A12',
                                          children='tag'),
                                     ...]),
                      ...])

这是我迄今为止的尝试,基于这样一种想法,即在嵌套列表中的每个位置,我们可能有一个终端节点,在这种情况下,我们只需将其添加到根节点,或者是一个非终端节点,在这种情况下我们提取相应的根标签并递归地遍历子标签。当列表为空时,我们将控制权返回给调用者。 我的感觉是编码风格可能不是最适合 Python,但我想更具体地知道我缺少什么。

def is_terminal(e):
    return len(e) == 2 and type(e[0]) == str and type(e[1]) == str

def from_list(lst, root):
    lst = list(lst) # avoid mutating input list
    if not lst:
        return
    for e in lst:
        if is_terminal(e):
            tag, children = e
            print "terminal", tag, "with root", root.tag
            root.add_child(Node(tag=tag, children=children, parent=root))
        else:
            e = list(e)
            tag, children = e.pop(0), e
            print "non terminal", tag, "with root", root.tag
            root = Node(tag=tag, parent=root)
            from_list(children, root=root)

它有很多问题。例如,它丢失了最高根'A' -i.e. A2 获取 A1 作为根。它还将树展平为具有 16 个子节点的节点,每个终端节点一个,并进入无限递归。 我会很感激任何类型的提示。

【问题讨论】:

    标签: python recursion tree


    【解决方案1】:

    我终于找到了问题所在,结果部分是算法中的缺失点,部分是对 Python 列表工作方式的误解。

    算法丢失了最高根的跟踪,因为我没有在else 语句中添加子根。这个新版本解决了这个问题。

    else:
        e = list(e)
        tag, children = e.pop(0), e
        print "non terminal", tag, "with root", root.tag
        subroot = Node(tag=tag, parent=root)
        root.add_child(subroot) #
        from_list(children, root=subroot)
    

    扁平化的问题实际上是我在Node 类定义中使用[] 作为默认参数。正如here 解释的那样,默认的空列表仅在第一次函数调用(或本例中的类实例化)时创建,而不是在每次调用函数时创建。 因此,根的子列表添加了所有子子列表(因此产生了扁平化效果),并且每次修改根子列表时都会修改所有子子列表的子列表 - 因此无限递归。

    事实证明,这更像是一个 Python 问题,而不是算法定义问题。

    作为记录,代码的完整更正版本:

    class Node(object):
        def __init__(self, tag, parent=None, children=None):
            self.tag = tag
            self.parent = parent
            self.children = [] if not children else children
            self.is_terminal = False if isinstance(self.children, list) else True
    
        def add_child(self, node):
            if not isinstance(node, Node):
                raise ValueError("Cannot append node of type: [%s]" % type(node))
            if self.is_terminal:
                raise ValueError("Cannot append node to terminal")
            else:
                self.children.append(node)
    
    def is_terminal(e):
        return len(e) == 2 and type(e[0]) == str and type(e[1]) == str
    
    def from_list(lst, root):
        lst = list(lst)
        if not lst:
            return
        for e in lst:
            if is_terminal(e):
                tag, children = e
                print "terminal", tag, "with root", root.tag
                root.add_child(Node(tag=tag, children=children, parent=root))
            else:
                e = list(e)
                tag, children = e.pop(0), e
                print "non terminal", tag, "with root", root.tag
                newroot = Node(tag=tag, parent=root)
                root.add_child(newroot)
                from_list(children, root=newroot)
    

    你是这样称呼它的:

    root = Node(tag=ex[0])
    from_list(ex[1:], root=root)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-12
      • 1970-01-01
      • 2018-08-12
      • 1970-01-01
      • 2020-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多