简体   繁体   English

在python类中实现树的递归函数

[英]Implementing recursive functions for trees in python class

I have created a class Tree and a class Node . 我创建了一个类Tree和一个Node类。 Now I needed to implement preOrder , postOrder and inOrder traversals. 现在我需要实现preOrderpostOrderinOrder遍历。 I did it using private functions. 我是用私人功能做的。 But is there a way to do the same using only one function? 但有没有办法只使用一个函数来做同样的事情?

class Node:
    def __init__(self, data):
        self.left = None
        self.right = None
        self.data = data

class Tree:
    def __init__(self):
        self.root = None

    # Private helper functions
    def __insert(self, data, root):
        if data < root.data:
            if root.left is None:
                root.left = Node(data)
            else:
                self.__insert(data, root.left)
        elif data >= root.data:
            if root.right is None:
                root.right = Node(data)
            else:
                self.__insert(data, root.right)

    # Traversals
    def __preOrder(self, root):
        print root.data
        if root.left:
            self.__preOrder(root.left)
        if root.right:
            self.__preOrder(root.right)

    # Wrapper Functions 
    def insert(self, data):
        if self.root == None:
            self.root = Node(data)
        else:
            self.__insert(data, self.root)

    def preOrder(self):
        self.__preOrder(self.root)


tree = Tree()
print "Enter elements to be inserted in the tree(End with a -1): "
while True:
    elem = int(raw_input())
    if elem == -1:
        break
    tree.insert(elem)

print "Preorder traversal: "
tree.preOrder()

Here I have to use the helper functions because I don't want the user to be providing the root element explicitly. 这里我必须使用辅助函数,因为我不希望用户显式提供根元素。

Yes, you can implement all 3 types of traversal in a single function. 是的,您可以在单个函数中实现所有3种类型的遍历。 I've turned the traversal functions into generators to make them more versatile. 我已经将遍历函数转换为生成器,以使它们更加通用。 So instead of printing their data they are iterators that yield their data. 因此,它们不是打印数据,而是生成数据的迭代器。 This lets you process the data as it's yielded, or you can capture it into a list (or other collection). 这使您可以在数据生成时处理数据,也可以将数据捕获到列表(或其他集合)中。

In Python 2 your classes should inherit from object , otherwise you get old-style classes, which are rather limited compared to new-style classes (Python 3 only has new-style classes). 在Python 2中,您的类应该继承自object ,否则您将获得旧式类,与新式类相比,它们相当有限(Python 3只有新式类)。

BTW, there's no need to use double underscores for the private functions (which invokes Python's name-mangling machinery), a single leading underscore is adequate. 顺便说一句,没有必要为私有函数(调用Python的名称修改机制)使用双下划线,单个前导下划线就足够了。

I've also added simple __repr__ methods to the classes, which can be handy during development & debugging. 我还在类中添加了简单的__repr__方法,这在开发和调试过程中非常方便。

class Node(object):
    def __init__(self, data):
        self.left = None
        self.right = None
        self.data = data

    def __repr__(self):
        return repr((self.data, self.left, self.right))


class Tree(object):
    def __init__(self):
        self.root = None

    def __repr__(self):
        return repr(self.root)

    # Private helper functions
    def _insert(self, data, root):
        if data < root.data:
            if root.left is None:
                root.left = Node(data)
            else:
                self._insert(data, root.left)
        else: # data >= root.data:
            if root.right is None:
                root.right = Node(data)
            else:
                self._insert(data, root.right)

    def _traverse(self, root, mode):
        if mode == 'pre':
            yield root.data
        if root.left:
            for u in self._traverse(root.left, mode):
                yield u
        if mode == 'in':
            yield root.data
        if root.right:
            for u in self._traverse(root.right, mode):
                yield u
        if mode == 'post':
            yield root.data

    # Wrapper Functions 
    def insert(self, data):
        if self.root == None:
            self.root = Node(data)
        else:
            self._insert(data, self.root)

    def preOrder(self):
        for u in self._traverse(self.root, 'pre'):
            yield u

    def inOrder(self):
        for u in self._traverse(self.root, 'in'):
            yield u

    def postOrder(self):
        for u in self._traverse(self.root, 'post'):
            yield u

# Test

tree = Tree()

for elem in '31415926':
    tree.insert(elem)

print tree

print "Preorder traversal: "
print list(tree.preOrder())

print "InOrder Traversal: "
print list(tree.inOrder())

print "PostOrder Traversal: "
print list(tree.postOrder())

output 产量

('3', ('1', None, ('1', None, ('2', None, None))), ('4', None, ('5', None, ('9', ('6', None, None), None))))
Preorder traversal: 
['3', '1', '1', '2', '4', '5', '9', '6']
InOrder Traversal: 
['1', '1', '2', '3', '4', '5', '6', '9']
PostOrder Traversal: 
['2', '1', '1', '6', '9', '5', '4', '3']

Here's an example of processing the data as it's yielded: 以下是处理数据时的示例:

for data in tree.inOrder():
    print data

FWIW, this code would be a lot cleaner in Python 3 because we could use the yield from syntax instead of those for loops. FWIW,这段代码在Python 3中会更加清晰,因为我们可以使用语法的yield from而不是for循环。 So instead of 而不是

for u in self._traverse(root.left, mode):
    yield u

we could do 我们能做到

yield from self._traverse(root.left, mode)

I'm not sure about implementing the traversal functions as one-liners, but an alternative approach to what you're trying to do would be to adapt the Strategy Pattern to your use case by abstracting the traversal logic into a series of separate classes that all inherit from one common TraversalStrategy . 我不确定将遍历函数实现为单行,但是您尝试做的另一种方法是通过将遍历逻辑抽象为一系列单独的类来使策略模式适应您的用例。所有都继承自一个常见的TraversalStrategy You can then inject a traversal strategy object as a dependency to Tree which decouples the structure of the tree from the logic used to traverse it. 然后,您可以将遍历策略对象注入为Tree的依赖项,从而将Tree的结构与用于遍历它的逻辑分离。

This approach has the following benefits: 这种方法有以下好处:

  • It allows you to get rid of the bulk of your current Tree methods 它允许您摆脱当前Tree方法的大部分
  • It increases the testability of your program (you can unit test tree traversal strategies in isolation from other tree-related logic) 它增加了程序的可测试性(您可以单独测试树遍历策略,与其他树相关的逻辑隔离)
  • It increases reusability (you can potentially use these traversal strategies in other situations) 它增加了可重用性(您可以在其他情况下使用这些遍历策略)
  • In general, it makes your program more SOLID : 一般来说,它使您的程序更加稳固
    • Your Tree class now has a Single Responsibility - a single reason to change 您的Tree类现在具有单一职责 - 改变的一个原因
    • You can implement new traversal behaviours for trees without having to touch the source code for Tree ie it's closed for modification 您可以为树实现新的遍历行为,而无需触及Tree的源代码,即它已关闭以进行修改
    • You're injecting the traversal logic as a dependency, so it's very easy to substitute between different implementations, including mocks 您将遍历逻辑注入为依赖关系,因此在不同的实现(包括模拟)之间进行替换非常容易

The code below has been written for Python 3, so it will need some minor changes to work for Python 2. 下面的代码是为Python 3编写的,因此需要对Python 2进行一些小的更改。

from abc import ABC, abstractmethod


class Node:
    def __init__(self, data):
        self.left = None
        self.right = None
        self.data = data

    def __repr__(self):
        return str(self.data)


class Tree:
    def __init__(self, traversal_strategy):
        self.root = None
        self.traversal_strategy = traversal_strategy

    def insert(self, data):
        if self.root is None:
            self.root = Node(data)
        else:
            self.__insert(data, self.root)

    def __insert(self, data, root):
        if data < root.data:
            if root.left is None:
                root.left = Node(data)
            else:
                self.__insert(data, root.left)
        elif data >= root.data:
            if root.right is None:
                root.right = Node(data)
            else:
                self.__insert(data, root.right)

    def traverse(self):
        self.traversal_strategy.traverse(self.root)


class TraversalStrategy(ABC):
    @abstractmethod
    def traverse(self, node):
        pass

    def _attempt_traverse(self, node):
        if node:
            self.traverse(node)


class PreOrderTraversal(TraversalStrategy):
    def traverse(self, node):
        print(node)
        self._attempt_traverse(node.left)
        self._attempt_traverse(node.right)


class InOrderTraversal(TraversalStrategy):
    def traverse(self, node):
        self._attempt_traverse(node.left)
        print(node)
        self._attempt_traverse(node.right)


class PostOrderTraversal(TraversalStrategy):
    def traverse(self, node):
        self._attempt_traverse(node.left)
        self._attempt_traverse(node.right)
        print(node)


def build_tree(traversal_strategy):
    tree = Tree(traversal_strategy)
    elements = [1, 3, 6, 9, 2, 8]

    for element in elements:
        tree.insert(element)

    return tree


if __name__ == '__main__':
    pre_order_tree = build_tree(PreOrderTraversal())
    in_order_tree = build_tree(InOrderTraversal())
    post_order_tree = build_tree(PostOrderTraversal())

    print('Pre order traversal: ')
    pre_order_tree.traverse()
    print()

    print('In order traversal: ')
    in_order_tree.traverse()
    print()

    print('Post order traversal: ')
    post_order_tree.traverse()
    print()

Output 产量

Pre order traversal: 
1
3
2
6
9
8

In order traversal: 
1
2
3
6
8
9

Post order traversal: 
2
8
9
6
3
1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM