【问题标题】:Breadth First Tree Traversal using Generators in Python在 Python 中使用生成器进行广度优先树遍历
【发布时间】:2018-10-22 17:53:52
【问题描述】:

我正在学习如何在 David Beazly 的优秀 Python Cookbook 文本中使用 Python 中的生成器。以下代码配方非常优雅地使用生成器定义了深度优先树遍历:

# example.py
#
# Example of depth-first search using a generator

class Node:
    def __init__(self, value):
        self._value = value
        self._children = []

    def __repr__(self):
        return 'Node({!r})'.format(self._value)

    def add_child(self, node):
        self._children.append(node)

    def __iter__(self):
        return iter(self._children)

    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()

# Example
if __name__ == '__main__':
    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    child1.add_child(Node(3))
    child1.add_child(Node(4))
    child2.add_child(Node(5))

    for ch in root.depth_first():
        print(ch)
    # Outputs: Node(0), Node(1), Node(3), Node(4), Node(2), Node(5)

我正在尝试想出一个同样优雅的方法

def breadth_first(self):
    pass

我故意不发布我一直在尝试的疯狂内容,因为我尝试过的所有内容都需要在其中保持“状态”。我不想使用传统的基于队列的解决方案。这个学术练习的重点是深入了解生成器的行为方式。因此,我想为上面的树创建一个并行的“breadth_first”方法。

欢迎任何指针/解决方案。

【问题讨论】:

    标签: python-3.x generator breadth-first-search yield-from


    【解决方案1】:

    如果没有一些严重的黑客攻击,你不能对bfs 使用递归(堆栈),但是队列可以工作:

    def breadth_first(self):
        q = [self]
        while q:
            n = q.pop(0)
            yield n
            for c in n._children:
                q.append(c)
    

    【讨论】:

    • 我尝试了您的解决方案。它工作正常:)。我希望我能像大卫的 depth_first 方法一样优雅地做到这一点,但不能。哦。好吧。
    【解决方案2】:

    您实现的深度优先解决方案本质上是“迭代然后递归”

    def depth_first(self):
        yield self
        for c in self.children:
            yield from c.depth_first():
    

    this blog post the activestate post it references 的启发,您获得了广度优先搜索,即“递归然后迭代”:

    def breadth_first(self):
        yield self
        for c in self.breadth_first():
            if not c.children:
                return  # stop the recursion as soon as we hit a leaf
            yield from c.children
    

    编辑:事实证明链接的博客说“没有终止检查”,替换为一个 activestate 链接,我已经适应使用上面的yield from

    【讨论】:

    • 如何避免breadth_first 的无限递归?在抱怨达到最大递归深度之前,它似乎只会一遍又一遍地产生self
    • 如果self 是非循环的(它代表树结构),BFS 将终止。你不能在树中“向上”遍历。
    • 看起来这确实在到达所有节点后永远递归......
    • 我添加了缺少的终止条件。
    【解决方案3】:

    我发现它既有用又优雅,一次生成一个级别。 Python 3 生成器如下:

    def get_level(t: Node) -> Iterable[List]:
        curr_level = [t] if t else []
        while len(curr_level) > 0:
            yield [node._value for node in curr_level]
            curr_level = [child for parent in curr_level
                                for child in parent._children
                                if child]
    

    【讨论】:

      【解决方案4】:

      如果itertools 有:

      # zip_chain('ABCD', 'xy') --> A x B y C D
      

      几乎是itertools.chain(itertools.zip_longest()),但不完全是。

      无论如何,zip_chain 允许:

      def bf(self):
          yield self
          yield from zip_chain(*map(Node.bf, self.children))
      

      我认为它也不会一次创建一整行

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-03
        • 1970-01-01
        相关资源
        最近更新 更多