如何在Python中实现树?Python中是否有类似Java的内置数据结构?

vis*_*hnu 164 python tree data-structures python-3.x

我正在尝试构建一个通用树.Python中是否有内置的数据结构来实现树?

小智 175

anytree

我推荐https://pypi.python.org/pypi/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'))
Run Code Online (Sandbox Code Playgroud)

特征

anytree还有一个强大的API:

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

  • 这是一个很好的形式,披露你是你在答案中推荐的软件包的作者. (54认同)
  • 只是最好的答案,其他人正在重新发明轮子. (22认同)
  • 这并没有回答问题,而只是为您自己的项目做广告,但由于某种原因,这是谷歌的最佳结果。问题是如何实现树,而不是如何使用您的实现。 (4认同)
  • @Ondrej 好吧,其他答案的依赖性较少,原始问题确实询问了内置数据结构。虽然 `anytree` 可能是一个很棒的库,但这是一个 Python 问题,而不是 Node.js 问题。 (3认同)
  • 他的开源实现是一个值得学习的优秀例子 (3认同)
  • @ c0fec0de我爱你!这个库很棒,甚至具有可视化功能 (2认同)

Gre*_*ill 100

Python没有像Java那样广泛的"内置"数据结构.但是,由于Python是动态的,因此很容易创建通用树.例如,二叉树可能是:

class Tree:
    def __init__(self):
        self.left = None
        self.right = None
        self.data = None
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

root = Tree()
root.data = "root"
root.left = Tree()
root.left.data = "left"
root.right = Tree()
root.right.data = "right"
Run Code Online (Sandbox Code Playgroud)

  • 这并没有真正解释有关实现有用的树实现. (101认同)
  • @platzhirsch:请完整阅读并引用该指南:Google明确指出,这是Python 2代码按预期工作所必需的,并建议改进与Py3的兼容性.这里我们谈论Py3代码.没有必要做额外的遗留打字. (14认同)
  • 这个问题用Python3标记,不需要从对象派生`class Tree` (13认同)
  • 那是一棵二叉树,不是一般的要求. (10认同)
  • @cfi从`object`派生有时只是一个指导原则:*如果一个类继承自其他基类,则显式继承自object.这也适用于嵌套类.*请参阅[Google Python样式指南](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Classes) (3认同)

Rud*_*ura 43

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

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')])])
Run Code Online (Sandbox Code Playgroud)

  • 按儿童指数。在这种情况下,Left 将始终是 children[0]。 (6认同)
  • 太神奇了,这也可以很容易地用作图表!我看到的唯一问题是:如何区分左节点和右节点? (2认同)

Ib3*_*33X 35

你可以试试:

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

正如这里建议的那样:https://gist.github.com/2012250


小智 17

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()
Run Code Online (Sandbox Code Playgroud)

  • 您是否可以添加一些注释来介绍您的代码和实现? (3认同)
  • 感谢您提供带有实用函数的完整二叉树实现。由于它是 Python 2,我为需要 Python 3 版本的人们创建了一个 [二叉树实现 (Py3)](https://gist.github.com/singh-abhijeet/09331fa57f598280c23b6f19b636cb39) 的要点。 (2认同)

Jus*_* R. 16

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

PyPi上还有很多可以浏览的实现.

如果我没记错的话,Python标准库不包含树数据结构,原因与.NET基类库不相同:内存的位置减少,导致更多的缓存未命中.在现代处理器上,将大量内存放入缓存通常会更快,而"指针丰富"的数据结构会使收益无效.

  • 仅供参考:互联网上充斥着对Boost的仇恨.显然它应该是一个巨大的痛苦,特别是因为它的支持已经停止.所以我建议远离那个 (2认同)

小智 9

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

tree={1:0, 2:0, 3:1, 4:2, 5:3}
Run Code Online (Sandbox Code Playgroud)

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

  • 另一种方法是使用列表的列表,其中列表中的第一个(或更多)元素是节点值,下面嵌套的两个列表表示其左右子树(或更多用于 n 叉树)。 (2认同)

Bru*_*uno 9

Greg Hewgill的答案很棒,但是如果每个级别需要更多节点,可以使用list | dictionary来创建它们:然后使用方法按名称或顺序访问它们(如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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

这应该足以让你开始弄清楚如何使这项工作


nat*_*usa 8

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
Run Code Online (Sandbox Code Playgroud)

作为字典工作,但提供你想要的尽可能多的嵌套dicts.请尝试以下方法:

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']            = '*'
Run Code Online (Sandbox Code Playgroud)

将提供一个嵌套的字典......确实可以作为树.

{'a': {'1': {'x': '@', 'y': '#'}, '2': {'x': '$'}, '3': '%'}, 'b': '*'}
Run Code Online (Sandbox Code Playgroud)

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

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

print(d['foo']['amy']['what']) # returns 'runs'
d['foo']['amy']['when'] = 'now' # add new branch
Run Code Online (Sandbox Code Playgroud)

这样,您可以根据需要继续编辑/添加/删除每个dict级别.所有用于遍历等的dict方法仍然适用.

  • 你有没有理由选择扩展`dict`而不是`defaultdict`?从我的测试中,扩展`defaultdict`而不是dict然后将`self.default_factory = type(self)`添加到init的顶部应该以相同的方式运行. (2认同)

Hug*_*aux 7

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

[root, [child_1, [[child_11, []], [child_12, []]], [child_2, []]]]
Run Code Online (Sandbox Code Playgroud)

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

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


Gal*_*eri 7

如果您已经在使用networkx库,那么您可以使用它来实现一棵树。

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

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

您可能想要实现一个平面树(又名有序树),其中每个同级都有唯一的排名,这通常是通过标记节点来完成的。

然而,语言看起来与语言不同,并且“生根”树状结构的方法通常是通过使用有向图来完成的,因此,虽然有一些非常酷的函数和相应的可视化可用,但如果您尚未使用 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')
Run Code Online (Sandbox Code Playgroud)

该库使每个节点可以成为任何可哈希对象,并且每个节点拥有的子节点数量没有限制。


gae*_*fan 6

我使用嵌套的dicts实现了树.它很容易做到,并且它为我提供了相当大的数据集.我在下面发布了一个示例,您可以在Google代码中看到更多内容

  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)
Run Code Online (Sandbox Code Playgroud)


小智 5

我已经在我的网站上发布了Python [3]树实现:http : //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)
Run Code Online (Sandbox Code Playgroud)