【问题标题】:How can I implement a tree in Python?如何在 Python 中实现树?
【发布时间】:2011-01-22 10:00:37
【问题描述】:

我正在尝试构建一棵通用树。

Python中有没有内置的数据结构来实现它?

【问题讨论】:

标签: python data-structures tree


【解决方案1】:

我推荐anytree(我是作者)。

例子:

from anytree import Node, RenderTree

udo = Node("Udo")
marc = Node("Marc", parent=udo)
lian = Node("Lian", parent=marc)
dan = Node("Dan", parent=udo)
jet = Node("Jet", parent=dan)
jan = Node("Jan", parent=dan)
joe = Node("Joe", parent=dan)

print(udo)
Node('/Udo')
print(joe)
Node('/Udo/Dan/Joe')

for pre, fill, node in RenderTree(udo):
    print("%s%s" % (pre, node.name))
Udo
├── Marc
│   └── Lian
└── Dan
    ├── Jet
    ├── Jan
    └── Joe

print(dan.children)
(Node('/Udo/Dan/Jet'), Node('/Udo/Dan/Jan'), Node('/Udo/Dan/Joe'))

anytree 还有一个强大的 API:

  • 简单的树创建
  • 简单的树修改
  • 预排序树迭代
  • 后序树迭代
  • 解析相对和绝对节点路径
  • 从一个节点走到另一个节点。
  • 树渲染(见上例)
  • 节点附加/分离连接

【讨论】:

  • 只是最好的答案,其他人正在重新发明轮子。
  • 在回答中披露您是您推荐的软件包的作者是一种很好的形式。
  • @c0fec0de 我爱你!!!!这个库很棒,甚至还有可视化功能
  • @Ondrej 其他答案是依赖较少的,最初的问题确实询问了内置数据结构。虽然 anytree 可能是一个很棒的库,但这是一个 python 问题,而不是 Node.js 问题。
  • 我通过谷歌找到了这个答案。这个图书馆真的很不错。我特别喜欢使用 mixin 类制作任何对象的树的能力!
【解决方案2】:

Python 没有 Java 那样广泛的“内置”数据结构。但是,因为 Python 是动态的,所以很容易创建通用树。例如,二叉树可能是:

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

你可以这样使用它:

root = Tree()
root.data = "root"
root.left = Tree()
root.left.data = "left"
root.right = Tree()
root.right.data = "right"

如果每个节点需要任意数量的子节点,则使用子节点列表:

class Tree:
    def __init__(self, data):
        self.children = []
        self.data = data

left = Tree("left")
middle = Tree("middle")
right = Tree("right")
root = Tree("root")
root.children = [left, middle, right]

【讨论】:

  • 这并没有真正解释如何制作一个有用的树实现。
  • 问题是用Python3标记的,那么不需要从对象中派生class Tree
  • @cfi 从object 派生有时只是一个指导原则:如果一个类没有继承自其他基类,则显式继承自对象。这也适用于嵌套类。 请参阅 Google Python Style Guide
  • @platzhirsch:请完整阅读并引用该指南:Google 明确指出这是 Python 2 代码按预期工作所必需的,并建议这样做以提高与 Py3 的兼容性。这里我们谈论的是 Py3 代码。无需进行额外的传统打字。
  • 这是一棵二叉树,而不是要求的一般树。
【解决方案3】:

通用树是具有零个或多个子节点的节点,每个子节点都是正确的(树)节点。它与二叉树不同,它们是不同的数据结构,尽管两者共享一些术语。

Python 中没有任何用于泛型树的内置数据结构,但可以通过类轻松实现。

class Tree(object):
    "Generic tree node."
    def __init__(self, name='root', children=None):
        self.name = name
        self.children = []
        if children is not None:
            for child in children:
                self.add_child(child)
    def __repr__(self):
        return self.name
    def add_child(self, node):
        assert isinstance(node, Tree)
        self.children.append(node)
#    *
#   /|\
#  1 2 +
#     / \
#    3   4
t = Tree('*', [Tree('1'),
               Tree('2'),
               Tree('+', [Tree('3'),
                          Tree('4')])])

【讨论】:

  • 太棒了,这也可以很容易地用作图表!我看到的唯一问题是:如何区分左节点和右节点?
  • 按儿童索引。在这种情况下,Left 将永远是 children[0]。
【解决方案4】:

你可以试试:

from collections import defaultdict
def tree(): return defaultdict(tree)
users = tree()
users['harold']['username'] = 'hrldcpr'
users['handler']['username'] = 'matthandlersux'

这里建议:https://gist.github.com/2012250

【讨论】:

【解决方案5】:
class Node:
    """
    Class Node
    """
    def __init__(self, value):
        self.left = None
        self.data = value
        self.right = None

class Tree:
    """
    Class tree will provide a tree as well as utility functions.
    """

    def createNode(self, data):
        """
        Utility function to create a node.
        """
        return Node(data)

    def insert(self, node , data):
        """
        Insert function will insert a node into tree.
        Duplicate keys are not allowed.
        """
        #if tree is empty , return a root node
        if node is None:
            return self.createNode(data)
        # if data is smaller than parent , insert it into left side
        if data < node.data:
            node.left = self.insert(node.left, data)
        elif data > node.data:
            node.right = self.insert(node.right, data)

        return node


    def search(self, node, data):
        """
        Search function will search a node into tree.
        """
        # if root is None or root is the search data.
        if node is None or node.data == data:
            return node

        if node.data < data:
            return self.search(node.right, data)
        else:
            return self.search(node.left, data)



    def deleteNode(self,node,data):
        """
        Delete function will delete a node into tree.
        Not complete , may need some more scenarion that we can handle
        Now it is handling only leaf.
        """

        # Check if tree is empty.
        if node is None:
            return None

        # searching key into BST.
        if data < node.data:
            node.left = self.deleteNode(node.left, data)
        elif data > node.data:
            node.right = self.deleteNode(node.right, data)
        else: # reach to the node that need to delete from BST.
            if node.left is None and node.right is None:
                del node
            if node.left == None:
                temp = node.right
                del node
                return  temp
            elif node.right == None:
                temp = node.left
                del node
                return temp

        return node

    def traverseInorder(self, root):
        """
        traverse function will print all the node in the tree.
        """
        if root is not None:
            self.traverseInorder(root.left)
            print(root.data)
            self.traverseInorder(root.right)

    def traversePreorder(self, root):
        """
        traverse function will print all the node in the tree.
        """
        if root is not None:
            print(root.data)
            self.traversePreorder(root.left)
            self.traversePreorder(root.right)

    def traversePostorder(self, root):
        """
        traverse function will print all the node in the tree.
        """
        if root is not None:
            self.traversePostorder(root.left)
            self.traversePostorder(root.right)
            print(root.data)


def main():
    root = None
    tree = Tree()
    root = tree.insert(root, 10)
    print(root)
    tree.insert(root, 20)
    tree.insert(root, 30)
    tree.insert(root, 40)
    tree.insert(root, 70)
    tree.insert(root, 60)
    tree.insert(root, 80)

    print("Traverse Inorder")
    tree.traverseInorder(root)

    print("Traverse Preorder")
    tree.traversePreorder(root)

    print("Traverse Postorder")
    tree.traversePostorder(root)


if __name__ == "__main__":
    main()

【讨论】:

  • 你能添加一些注释来介绍你的代码和你的实现吗?
  • 感谢使用实用函数的完整二叉树实现。由于它是 Python 2,我为需要 Python 3 版本的人创建了 Binary Tree implementation (Py3) 的要点。
【解决方案6】:

没有内置树,但您可以通过从 List 子类化 Node 类型并编写遍历方法来轻松构建树。如果你这样做,我发现bisect 很有用。

PyPi 上也有很多实现可以浏览。

如果我没记错的话,Python 标准库不包含树数据结构的原因与 .NET 基类库不包含树数据结构的原因相同:减少了内存的局部性,从而导致更多的缓存未命中。在现代处理器上,将大块内存放入缓存通常会更快,而“指针丰富”的数据结构会抵消这种好处。

【讨论】:

  • 仅供参考:互联网上充斥着对 Boost 的仇恨。显然,处理它应该是一个巨大的痛苦,特别是因为对它的支持已经停止。所以我建议远离那个
  • 谢谢。我个人没有遇到任何麻烦,但我不想误导,所以我删除了该引用。
【解决方案7】:

我将根树实现为字典{child:parent}。例如,对于根节点0,一棵树可能看起来像这样:

tree={1:0, 2:0, 3:1, 4:2, 5:3}

这种结构很容易沿着从任何节点到根的路径向上移动,这与我正在处理的问题相关。

【讨论】:

  • 这是我考虑的方式,直到我看到答案。虽然由于一棵树是有两个孩子的父母,如果你想下去,你可以做{parent:[leftchild,rightchild]}
  • 另一种方法是使用列表的列表,其中列表中的第一个(或更多)元素是节点值,以下嵌套的两个列表表示其左右子树(或更多用于 n- ary 树)。
【解决方案8】:

Greg Hewgill 的回答很棒,但如果您需要每个级别的更多节点,您可以使用列表|字典来创建它们:然后使用方法按名称或顺序(如 id)访问它们

class node(object):
    def __init__(self):
        self.name=None
        self.node=[]
        self.otherInfo = None
        self.prev=None
    def nex(self,child):
        "Gets a node by number"
        return self.node[child]
    def prev(self):
        return self.prev
    def goto(self,data):
        "Gets the node by name"
        for child in range(0,len(self.node)):
            if(self.node[child].name==data):
                return self.node[child]
    def add(self):
        node1=node()
        self.node.append(node1)
        node1.prev=self
        return node1

现在只需创建一个根并构建它: 例如:

tree=node()  #create a node
tree.name="root" #name it root
tree.otherInfo="blue" #or what ever 
tree=tree.add() #add a node to the root
tree.name="node1" #name it

    root
   /
child1

tree=tree.add()
tree.name="grandchild1"

       root
      /
   child1
   /
grandchild1

tree=tree.prev()
tree=tree.add()
tree.name="gchild2"

          root
           /
        child1
        /    \
grandchild1 gchild2

tree=tree.prev()
tree=tree.prev()
tree=tree.add()
tree=tree.name="child2"

              root
             /   \
        child1  child2
       /     \
grandchild1 gchild2


tree=tree.prev()
tree=tree.goto("child1") or tree=tree.nex(0)
tree.name="changed"

              root
              /   \
         changed   child2
        /      \
  grandchild1  gchild2

这应该足以让您开始弄清楚如何进行这项工作

【讨论】:

  • 此答案中缺少某些内容,过去 2 天我一直在尝试此解决方案,我认为您在对象添加方法中有一些逻辑流程。我将提交我对这个问题的答案,请检查一下,如果我能提供帮助,请告诉我。
【解决方案9】:
class Tree(dict):
    """A tree implementation using python's autovivification feature."""
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    #cast a (nested) dict to a (nested) Tree class
    def __init__(self, data={}):
        for k, data in data.items():
            if isinstance(data, dict):
                self[k] = type(self)(data)
            else:
                self[k] = data

作为字典工作,但提供尽可能多的嵌套字典。 请尝试以下操作:

your_tree = Tree()

your_tree['a']['1']['x']  = '@'
your_tree['a']['1']['y']  = '#'
your_tree['a']['2']['x']  = '$'
your_tree['a']['3']       = '%'
your_tree['b']            = '*'

将提供一个嵌套的 dict ... 确实像一棵树一样工作。

{'a': {'1': {'x': '@', 'y': '#'}, '2': {'x': '$'}, '3': '%'}, 'b': '*'}

...如果您已经有一个字典,它会将每个级别转换为一棵树:

d = {'foo': {'amy': {'what': 'runs'} } }
tree = Tree(d)

print(d['foo']['amy']['what']) # returns 'runs'
d['foo']['amy']['when'] = 'now' # add new branch

通过这种方式,您可以根据需要保持编辑/添加/删除每个字典级别。 用于遍历等的所有 dict 方法仍然适用。

【讨论】:

  • 你选择扩展dict而不是defaultdict有什么原因吗?根据我的测试,扩展 defaultdict 而不是 dict 然后将 self.default_factory = type(self) 添加到 init 的顶部应该以相同的方式运行。
  • 我可能在这里遗漏了一些东西,你如何浏览这个结构?例如,从孩子到父母或兄弟姐妹似乎很难
【解决方案10】:

如果有人需要更简单的方法来做到这一点,一棵树只是一个递归嵌套的列表(因为集合是不可散列的):

[root, [child_1, [[child_11, []], [child_12, []]], [child_2, []]]]

每个分支都是一对:[ object, [children] ]
每片叶子都是一对:[ object, [] ]

但是如果你需要一个有方法的类,你可以使用anytree。

【讨论】:

    【解决方案11】:

    我已经使用嵌套字典实现了树。这很容易做到,并且对我来说非常大的数据集很有效。我在下面发布了一个示例,您可以在Google code查看更多内容

      def addBallotToTree(self, tree, ballotIndex, ballot=""):
        """Add one ballot to the tree.
    
        The root of the tree is a dictionary that has as keys the indicies of all 
        continuing and winning candidates.  For each candidate, the value is also
        a dictionary, and the keys of that dictionary include "n" and "bi".
        tree[c]["n"] is the number of ballots that rank candidate c first.
        tree[c]["bi"] is a list of ballot indices where the ballots rank c first.
    
        If candidate c is a winning candidate, then that portion of the tree is
        expanded to indicate the breakdown of the subsequently ranked candidates.
        In this situation, additional keys are added to the tree[c] dictionary
        corresponding to subsequently ranked candidates.
        tree[c]["n"] is the number of ballots that rank candidate c first.
        tree[c]["bi"] is a list of ballot indices where the ballots rank c first.
        tree[c][d]["n"] is the number of ballots that rank c first and d second.
        tree[c][d]["bi"] is a list of the corresponding ballot indices.
    
        Where the second ranked candidates is also a winner, then the tree is 
        expanded to the next level.  
    
        Losing candidates are ignored and treated as if they do not appear on the 
        ballots.  For example, tree[c][d]["n"] is the total number of ballots
        where candidate c is the first non-losing candidate, c is a winner, and
        d is the next non-losing candidate.  This will include the following
        ballots, where x represents a losing candidate:
        [c d]
        [x c d]
        [c x d]
        [x c x x d]
    
        During the count, the tree is dynamically updated as candidates change
        their status.  The parameter "tree" to this method may be the root of the
        tree or may be a sub-tree.
        """
    
        if ballot == "":
          # Add the complete ballot to the tree
          weight, ballot = self.b.getWeightedBallot(ballotIndex)
        else:
          # When ballot is not "", we are adding a truncated ballot to the tree,
          # because a higher-ranked candidate is a winner.
          weight = self.b.getWeight(ballotIndex)
    
        # Get the top choice among candidates still in the running
        # Note that we can't use Ballots.getTopChoiceFromWeightedBallot since
        # we are looking for the top choice over a truncated ballot.
        for c in ballot:
          if c in self.continuing | self.winners:
            break # c is the top choice so stop
        else:
          c = None # no candidates left on this ballot
    
        if c is None:
          # This will happen if the ballot contains only winning and losing
          # candidates.  The ballot index will not need to be transferred
          # again so it can be thrown away.
          return
    
        # Create space if necessary.
        if not tree.has_key(c):
          tree[c] = {}
          tree[c]["n"] = 0
          tree[c]["bi"] = []
    
        tree[c]["n"] += weight
    
        if c in self.winners:
          # Because candidate is a winner, a portion of the ballot goes to
          # the next candidate.  Pass on a truncated ballot so that the same
          # candidate doesn't get counted twice.
          i = ballot.index(c)
          ballot2 = ballot[i+1:]
          self.addBallotToTree(tree[c], ballotIndex, ballot2)
        else:
          # Candidate is in continuing so we stop here.
          tree[c]["bi"].append(ballotIndex)
    

    【讨论】:

      【解决方案12】:

      我在我的网站上发布了一个 Python 3 树实现:https://web.archive.org/web/20120723175438/www.quesucede.com/page/show/id/python_3_tree_implementation

      代码如下:

      import uuid
      
      def sanitize_id(id):
          return id.strip().replace(" ", "")
      
      (_ADD, _DELETE, _INSERT) = range(3)
      (_ROOT, _DEPTH, _WIDTH) = range(3)
      
      class Node:
      
          def __init__(self, name, identifier=None, expanded=True):
              self.__identifier = (str(uuid.uuid1()) if identifier is None else
                      sanitize_id(str(identifier)))
              self.name = name
              self.expanded = expanded
              self.__bpointer = None
              self.__fpointer = []
      
          @property
          def identifier(self):
              return self.__identifier
      
          @property
          def bpointer(self):
              return self.__bpointer
      
          @bpointer.setter
          def bpointer(self, value):
              if value is not None:
                  self.__bpointer = sanitize_id(value)
      
          @property
          def fpointer(self):
              return self.__fpointer
      
          def update_fpointer(self, identifier, mode=_ADD):
              if mode is _ADD:
                  self.__fpointer.append(sanitize_id(identifier))
              elif mode is _DELETE:
                  self.__fpointer.remove(sanitize_id(identifier))
              elif mode is _INSERT:
                  self.__fpointer = [sanitize_id(identifier)]
      
      class Tree:
      
          def __init__(self):
              self.nodes = []
      
          def get_index(self, position):
              for index, node in enumerate(self.nodes):
                  if node.identifier == position:
                      break
              return index
      
          def create_node(self, name, identifier=None, parent=None):
      
              node = Node(name, identifier)
              self.nodes.append(node)
              self.__update_fpointer(parent, node.identifier, _ADD)
              node.bpointer = parent
              return node
      
          def show(self, position, level=_ROOT):
              queue = self[position].fpointer
              if level == _ROOT:
                  print("{0} [{1}]".format(self[position].name,
                                           self[position].identifier))
              else:
                  print("\t"*level, "{0} [{1}]".format(self[position].name,
                                                       self[position].identifier))
              if self[position].expanded:
                  level += 1
                  for element in queue:
                      self.show(element, level)  # recursive call
      
          def expand_tree(self, position, mode=_DEPTH):
              # Python generator. Loosly based on an algorithm from 'Essential LISP' by
              # John R. Anderson, Albert T. Corbett, and Brian J. Reiser, page 239-241
              yield position
              queue = self[position].fpointer
              while queue:
                  yield queue[0]
                  expansion = self[queue[0]].fpointer
                  if mode is _DEPTH:
                      queue = expansion + queue[1:]  # depth-first
                  elif mode is _WIDTH:
                      queue = queue[1:] + expansion  # width-first
      
          def is_branch(self, position):
              return self[position].fpointer
      
          def __update_fpointer(self, position, identifier, mode):
              if position is None:
                  return
              else:
                  self[position].update_fpointer(identifier, mode)
      
          def __update_bpointer(self, position, identifier):
              self[position].bpointer = identifier
      
          def __getitem__(self, key):
              return self.nodes[self.get_index(key)]
      
          def __setitem__(self, key, item):
              self.nodes[self.get_index(key)] = item
      
          def __len__(self):
              return len(self.nodes)
      
          def __contains__(self, identifier):
              return [node.identifier for node in self.nodes
                      if node.identifier is identifier]
      
      if __name__ == "__main__":
      
          tree = Tree()
          tree.create_node("Harry", "harry")  # root node
          tree.create_node("Jane", "jane", parent = "harry")
          tree.create_node("Bill", "bill", parent = "harry")
          tree.create_node("Joe", "joe", parent = "jane")
          tree.create_node("Diane", "diane", parent = "jane")
          tree.create_node("George", "george", parent = "diane")
          tree.create_node("Mary", "mary", parent = "diane")
          tree.create_node("Jill", "jill", parent = "george")
          tree.create_node("Carol", "carol", parent = "jill")
          tree.create_node("Grace", "grace", parent = "bill")
          tree.create_node("Mark", "mark", parent = "jane")
      
          print("="*80)
          tree.show("harry")
          print("="*80)
          for node in tree.expand_tree("harry", mode=_WIDTH):
              print(node)
          print("="*80)
      

      【讨论】:

        【解决方案13】:

        如果您已经在使用networkx 库,那么您可以使用它来实现一棵树。否则,您可以尝试其他答案之一。

        NetworkX 是一个用于创建、操作和研究的 Python 包 复杂网络的结构、动力学和功能。

        因为“树”是(通常有根的)连通无环图的另一个术语,这些在 NetworkX 中称为“树状结构”。

        您可能想要实现一个平面树(也称为有序树),其中每个兄弟节点都有一个唯一的等级,这通常通过标记节点来完成。

        但是,graph 语言看起来与 tree 语言不同,通常使用有向图来“生根”树状结构,因此,虽然有一些非常酷的功能和相应的可视化可用,如果您还没有使用 networkx,它可能不是一个理想的选择。

        构建树的示例:

        import networkx as nx
        G = nx.Graph()
        G.add_edge('A', 'B')
        G.add_edge('B', 'C')
        G.add_edge('B', 'D')
        G.add_edge('A', 'E')
        G.add_edge('E', 'F')
        

        库使每个节点为any hashable object,并且每个节点的子节点数量没有限制。

        该库还提供与树相关的graph algorithmsvisualization 功能。

        【讨论】:

          【解决方案14】:

          您好,可以试试itertree(我是作者)。

          该包朝着 anytree 包的方向发展,但重点有所不同。在大树(>100000 个项目)上的性能要好得多,并且它处理迭代器以具有有效的过滤机制。

          >>>from itertree import *
          >>>root=iTree('root')
          
          >>># add some children:
          >>>root.append(iTree('Africa',data={'surface':30200000,'inhabitants':1257000000}))
          >>>root.append(iTree('Asia', data={'surface': 44600000, 'inhabitants': 4000000000}))
          >>>root.append(iTree('America', data={'surface': 42549000, 'inhabitants': 1009000000}))
          >>>root.append(iTree('Australia&Oceania', data={'surface': 8600000, 'inhabitants': 36000000}))
          >>>root.append(iTree('Europe', data={'surface': 10523000 , 'inhabitants': 746000000}))
          >>># you might use __iadd__ operator for adding too:
          >>>root+=iTree('Antarktika', data={'surface': 14000000, 'inhabitants': 1100})
          
          >>># for building next level we select per index:
          >>>root[0]+=iTree('Ghana',data={'surface':238537,'inhabitants':30950000})
          >>>root[0]+=iTree('Niger', data={'surface': 1267000, 'inhabitants': 23300000})
          >>>root[1]+=iTree('China', data={'surface': 9596961, 'inhabitants': 1411780000})
          >>>root[1]+=iTree('India', data={'surface': 3287263, 'inhabitants': 1380004000})
          >>>root[2]+=iTree('Canada', data={'type': 'country', 'surface': 9984670, 'inhabitants': 38008005})    
          >>>root[2]+=iTree('Mexico', data={'surface': 1972550, 'inhabitants': 127600000 })
          >>># extend multiple items:
          >>>root[3].extend([iTree('Australia', data={'surface': 7688287, 'inhabitants': 25700000 }), iTree('New Zealand', data={'surface': 269652, 'inhabitants': 4900000 })])
          >>>root[4]+=iTree('France', data={'surface': 632733, 'inhabitants': 67400000 }))
          >>># select parent per TagIdx - remember in itertree you might put items with same tag multiple times:
          >>>root[TagIdx('Europe'0)]+=iTree('Finland', data={'surface': 338465, 'inhabitants': 5536146 })
          

          可以渲染创建的树:

          >>>root.render()
          iTree('root')
               └──iTree('Africa', data=iTData({'surface': 30200000, 'inhabitants': 1257000000}))
                   └──iTree('Ghana', data=iTData({'surface': 238537, 'inhabitants': 30950000}))
                   └──iTree('Niger', data=iTData({'surface': 1267000, 'inhabitants': 23300000}))
               └──iTree('Asia', data=iTData({'surface': 44600000, 'inhabitants': 4000000000}))
                   └──iTree('China', data=iTData({'surface': 9596961,  'inhabitants': 1411780000}))
                   └──iTree('India', data=iTData({'surface': 3287263, 'inhabitants': 1380004000}))
               └──iTree('America', data=iTData({'surface': 42549000, 'inhabitants': 1009000000}))
                   └──iTree('Canada', data=iTData({'surface': 9984670, 'inhabitants': 38008005}))
                   └──iTree('Mexico', data=iTData({'surface': 1972550, 'inhabitants': 127600000}))
               └──iTree('Australia&Oceania', data=iTData({'surface': 8600000, 'inhabitants': 36000000}))
                   └──iTree('Australia', data=iTData({'surface': 7688287, 'inhabitants': 25700000}))
                   └──iTree('New Zealand', data=iTData({'surface': 269652, 'inhabitants': 4900000}))
               └──iTree('Europe', data=iTData({'surface': 10523000, 'inhabitants': 746000000}))
                   └──iTree('France', data=iTData({'surface': 632733, 'inhabitants': 67400000}))
                   └──iTree('Finland', data=iTData({'surface': 338465, 'inhabitants': 5536146}))
               └──iTree('Antarktika', data=iTData({'surface': 14000000, 'inhabitants': 1100}))
          

          例如过滤可以这样完成:

          >>>item_filter = Filter.iTFilterData(data_key='inhabitants', data_value=iTInterval(0, 20000000))
          >>>iterator=root.iter_all(item_filter=item_filter)
          >>>for i in iterator:
          >>>    print(i)
          iTree("'New Zealand'", data=iTData({'surface': 269652, 'inhabitants': 4900000}), subtree=[])
          iTree("'Finland'", data=iTData({'surface': 338465, 'inhabitants': 5536146}), subtree=[])
          iTree("'Antarktika'", data=iTData({'surface': 14000000, 'inhabitants': 1100}), subtree=[])
          

          【讨论】:

            【解决方案15】:

            另一个松散地基于Bruno's answer的树实现:

            class Node:
                def __init__(self):
                    self.name: str = ''
                    self.children: List[Node] = []
                    self.parent: Node = self
            
                def __getitem__(self, i: int) -> 'Node':
                    return self.children[i]
            
                def add_child(self):
                    child = Node()
                    self.children.append(child)
                    child.parent = self
                    return child
            
                def __str__(self) -> str:
                    def _get_character(x, left, right) -> str:
                        if x < left:
                            return '/'
                        elif x >= right:
                            return '\\'
                        else:
                            return '|'
            
                    if len(self.children):
                        children_lines: Sequence[List[str]] = list(map(lambda child: str(child).split('\n'), self.children))
                        widths: Sequence[int] = list(map(lambda child_lines: len(child_lines[0]), children_lines))
                        max_height: int = max(map(len, children_lines))
                        total_width: int = sum(widths) + len(widths) - 1
                        left: int = (total_width - len(self.name) + 1) // 2
                        right: int = left + len(self.name)
            
                        return '\n'.join((
                            self.name.center(total_width),
                            ' '.join(map(lambda width, position: _get_character(position - width // 2, left, right).center(width),
                                         widths, accumulate(widths, add))),
                            *map(
                                lambda row: ' '.join(map(
                                    lambda child_lines: child_lines[row] if row < len(child_lines) else ' ' * len(child_lines[0]),
                                    children_lines)),
                                range(max_height))))
                    else:
                        return self.name
            

            以及如何使用它的示例:

            tree = Node()
            tree.name = 'Root node'
            tree.add_child()
            tree[0].name = 'Child node 0'
            tree.add_child()
            tree[1].name = 'Child node 1'
            tree.add_child()
            tree[2].name = 'Child node 2'
            tree[1].add_child()
            tree[1][0].name = 'Grandchild 1.0'
            tree[2].add_child()
            tree[2][0].name = 'Grandchild 2.0'
            tree[2].add_child()
            tree[2][1].name = 'Grandchild 2.1'
            print(tree)
            

            应该输出:

            根节点 // \ 子节点 0 子节点 1 子节点 2 | / \ 孙子 1.0 孙子 2.0 孙子 2.1

            【讨论】:

            • 干净简单。您在__str__ 函数中缺少一些定义,即accumulationadd。但是树实现本身效果很好。
            • 应该是itertools.accumulateoperator.add,抱歉省略了。
            【解决方案16】:

            如果要创建树数据结构,则首先必须创建 treeElement 对象。如果您创建 treeElement 对象,那么您可以决定您的树的行为方式。

            下面是 TreeElement 类:

            class TreeElement (object):
            
            def __init__(self):
                self.elementName = None
                self.element = []
                self.previous = None
                self.elementScore = None
                self.elementParent = None
                self.elementPath = []
                self.treeLevel = 0
            
            def goto(self, data):
                for child in range(0, len(self.element)):
                    if (self.element[child].elementName == data):
                        return self.element[child]
            
            def add(self):
            
                single_element = TreeElement()
                single_element.elementName = self.elementName
                single_element.previous = self.elementParent
                single_element.elementScore = self.elementScore
                single_element.elementPath = self.elementPath
                single_element.treeLevel = self.treeLevel
            
                self.element.append(single_element)
            
                return single_element
            

            现在,我们必须使用这个元素来创建树,在这个例子中我使用的是 A* 树。

            class AStarAgent(Agent):
            # Initialization Function: Called one time when the game starts
            def registerInitialState(self, state):
                return;
            
            # GetAction Function: Called with every frame
            def getAction(self, state):
            
                # Sorting function for the queue
                def sortByHeuristic(each_element):
            
                    if each_element.elementScore:
                        individual_score = each_element.elementScore[0][0] + each_element.treeLevel
                    else:
                        individual_score = admissibleHeuristic(each_element)
            
                    return individual_score
            
                # check the game is over or not
                if state.isWin():
                    print('Job is done')
                    return Directions.STOP
                elif state.isLose():
                    print('you lost')
                    return Directions.STOP
            
                # Create empty list for the next states
                astar_queue = []
                astar_leaf_queue = []
                astar_tree_level = 0
                parent_tree_level = 0
            
                # Create Tree from the give node element
                astar_tree = TreeElement()
                astar_tree.elementName = state
                astar_tree.treeLevel = astar_tree_level
                astar_tree = astar_tree.add()
            
                # Add first element into the queue
                astar_queue.append(astar_tree)
            
                # Traverse all the elements of the queue
                while astar_queue:
            
                    # Sort the element from the queue
                    if len(astar_queue) > 1:
                        astar_queue.sort(key=lambda x: sortByHeuristic(x))
            
                    # Get the first node from the queue
                    astar_child_object = astar_queue.pop(0)
                    astar_child_state = astar_child_object.elementName
            
                    # get all legal actions for the current node
                    current_actions = astar_child_state.getLegalPacmanActions()
            
                    if current_actions:
            
                        # get all the successor state for these actions
                        for action in current_actions:
            
                            # Get the successor of the current node
                            next_state = astar_child_state.generatePacmanSuccessor(action)
            
                            if next_state:
            
                                # evaluate the successor states using scoreEvaluation heuristic
                                element_scored = [(admissibleHeuristic(next_state), action)]
            
                                # Increase the level for the child
                                parent_tree_level = astar_tree.goto(astar_child_state)
                                if parent_tree_level:
                                    astar_tree_level = parent_tree_level.treeLevel + 1
                                else:
                                    astar_tree_level += 1
            
                                # create tree for the finding the data
                                astar_tree.elementName = next_state
                                astar_tree.elementParent = astar_child_state
                                astar_tree.elementScore = element_scored
                                astar_tree.elementPath.append(astar_child_state)
                                astar_tree.treeLevel = astar_tree_level
                                astar_object = astar_tree.add()
            
                                # If the state exists then add that to the queue
                                astar_queue.append(astar_object)
            
                            else:
                                # Update the value leaf into the queue
                                astar_leaf_state = astar_tree.goto(astar_child_state)
                                astar_leaf_queue.append(astar_leaf_state)
            

            您可以从对象中添加/删除任何元素,但要确保结构完整。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-12-17
              • 2021-02-20
              • 2017-05-02
              • 2022-09-29
              相关资源
              最近更新 更多