Buscando una buena estructura de datos de árbol de Python [cerrado]

92

Estoy buscando una buena clase de estructura de datos de árbol. Me he encontrado con este paquete , pero como soy relativamente nuevo en Python (no en programación), no sé si hay alguno mejor.

Me gustaría escuchar a los Pythonistas aquí: ¿tiene un script de árbol favorito que usa regularmente y recomendaría?

[Editar]

Para aclarar, por 'Árbol', me refiero a un árbol simple y desordenado (Hmm, esa es una definición un poco recursiva, pero con suerte, eso aclara un poco las cosas). Con respecto a para qué necesito el árbol (es decir, caso de uso). Estoy leyendo datos de árbol de un archivo plano y necesito construir un árbol a partir de los datos y recorrer todos los nodos del árbol.

morfeo
fuente
1
Mientras tanto, hay una estructura de datos de árbol simple, liviana y extensible: anytree.readthedocs.io/en/latest
c0fec0de

Respuestas:

38

Enrolla el tuyo. Por ejemplo, simplemente modele su árbol como una lista de lista. Debe detallar su necesidad específica antes de que las personas puedan brindar una mejor recomendación.

En respuesta a la pregunta de HelloGoodbye, este es un código de muestra para iterar un árbol.

def walk(node):
    """ iterate tree in pre-order depth-first search order """
    yield node
    for child in node.children:
        for n in walk(child):
            yield n

Un problema es que esta implementación recursiva es O (n log n). Funciona bien para todos los árboles con los que tengo que lidiar. Quizás el subgenerador en Python 3 ayudaría.

Wai Yip Tung
fuente
¿Cómo se recorre todos los elementos de un árbol de este tipo de forma 'pitónica'?
Hola
Normalmente, itera un árbol utilizando DFS o BFS. Por lo general, implemento un generador que usa DFS como def walk (árbol): ...
Wai Yip Tung
1
¿Qué es DFS y BFS? Estos acrónimos son nuevos para mí.
Hola
1
Se agregó un código de muestra para DFS.
Wai Yip Tung
1
La búsqueda en profundidad significa que los hijos de un nodo se visitan antes que sus hermanos. así que si tienes "[A, [B, [C, D]], [E, [F, G]]]", entonces, asumiendo que visitas B antes que E, también visitas C y D antes de E. Amplitud- La primera búsqueda significa que todos los nodos en el mismo nivel se visitan antes que cualquiera de sus hijos, por lo que tanto B como E serían visitados antes de C, D, F o G.
Mark Reed
76

Puedes construir un buen árbol de dictados de dictados como este:

import collections

def Tree():
    return collections.defaultdict(Tree)

Puede que no sea exactamente lo que quieres, ¡pero es bastante útil! Los valores se guardan solo en los nodos hoja. A continuación, se muestra un ejemplo de cómo funciona:

>>> t = Tree()
>>> t
defaultdict(<function tree at 0x2142f50>, {})
>>> t[1] = "value"
>>> t[2][2] = "another value"
>>> t
defaultdict(<function tree at 0x2142f50>, {1: 'value', 2: defaultdict(<function tree at 0x2142f50>, {2: 'another value'})}) 

Para obtener más información, echa un vistazo a la esencia .

Stefan
fuente
1
¡Vaya, usar defaultdict es una idea brillante!
laike9m
Genial y siempre uso la pared try excepto con setters.
Jimmy Kane
5
una desventaja es que es muy complicado agregar métodos relacionados con las manipulaciones de árboles. también esto está en wiki y se llama autovivificación: en.wikipedia.org/wiki/Autovivification#Python
denfromufa
41

Encontré un módulo escrito por Brett Alistair Kromkamp que no se completó. Lo terminé y lo hice público en github y lo renombré como treelib(original pyTree):

https://github.com/caesar0301/treelib

Que te ayude ...

caesar0301
fuente
5
la licencia es GPL, qué lástima
denfromufa
12
Esta licencia fue otorgada cuando yo ni siquiera sabía lo que significaba una licencia. Sé que es un módulo simple pero útil. Desde la versión 1.3.0, lo redistribuyo bajo Licencia Apache. Ahora puede usarlo donde lo necesite con la declaración de los derechos de autor originales.
caesar0301
9

Sobre la base de la respuesta dada anteriormente con el árbol de una sola línea usando defaultdict , puede convertirlo en una clase. Esto le permitirá configurar valores predeterminados en un constructor y construir sobre él de otras formas.

class Tree(defaultdict):
    def __call__(self):
        return Tree(self)

    def __init__(self, parent):
        self.parent = parent
        self.default_factory = self

Este ejemplo le permite hacer una referencia posterior para que cada nodo pueda referirse a su padre en el árbol.

>>> t = Tree(None)
>>> t[0][1][2] = 3
>>> t
defaultdict(defaultdict(..., {...}), {0: defaultdict(defaultdict(..., {...}), {1: defaultdict(defaultdict(..., {...}), {2: 3})})})
>>> t[0][1].parent
defaultdict(defaultdict(..., {...}), {1: defaultdict(defaultdict(..., {...}), {2: 3})})
>>> t2 = t[0][1]
>>> t2
defaultdict(defaultdict(..., {...}), {2: 3})
>>> t2[2]
3

A continuación, incluso podría anular __setattr__ en la clase Tree para que al reasignar el padre, lo elimine como hijo de ese padre. Muchas cosas interesantes con este patrón.

Sandy Chapman
fuente
El padre se divide para t [0] [1] [2] en el ejemplo anterior. AttributeError: el objeto 'int' no tiene atributo 'parent'
MatthieuBizien
@oao Esto no está roto. Estás especificando t [0] [1] [2] = 3. Por lo tanto, t [0] [1] [2] no va a ser un tipo defaultdict, sino un tipo Number (como defaultdict se usa para establecer valores predeterminados para elementos faltantes). Si desea que sea un diccionario predeterminado, debe usar t [0] [1] [2] sin la asignación.
Sandy Chapman
7

Para un árbol con hijos ordenados, normalmente haría algo como esto (aunque un poco menos genérico, adaptado a lo que estoy haciendo):

class TreeNode(list):

    def __init__(self, iterable=(), **attributes):
        self.attr = attributes
        list.__init__(self, iterable)

    def __repr__(self):
        return '%s(%s, %r)' % (type(self).__name__, list.__repr__(self),
            self.attr)

Podría hacer algo comparable con a dicto usando DictMixino sus descendientes más modernos si desea que los niños desordenados accedan por clave.

Matt Anderson
fuente
7

Podría valer la pena escribir su propia envoltura de árbol basada en un gráfico dirigido acíclico usando la biblioteca networkx .

Andrew Walker
fuente
4

Aquí hay algo en lo que estaba trabajando.

class Tree:
    def __init__(self, value, *children):
        '''Singly linked tree, children do not know who their parent is.
        '''
        self.value = value
        self.children = tuple(children)

    @property
    def arguments(self):
        return (self.value,) + self.children

    def __eq__(self, tree):
        return self.arguments == tree.arguments

    def __repr__(self):
        argumentStr = ', '.join(map(repr, self.arguments))
        return '%s(%s)' % (self.__class__.__name__, argumentStr)

Use como tal (números usados ​​como valores de ejemplo): t = Tree(1, Tree(2, Tree(4)), Tree(3, Tree(5)))

Aaron Robson
fuente
3

¿ Ayudaría BTrees ? Son parte del código de la base de datos de objetos de Zope. Descargar todo el paquete ZODB es un poco exagerado, pero espero que el módulo BTrees sea al menos algo separable.

Jenn D.
fuente
2

Creo, por mi propia experiencia en problemas con estructuras de datos más avanzadas, que lo más importante que puede hacer aquí es obtener un buen conocimiento sobre el concepto general de árboles como estructuras de datos. Si comprende el mecanismo básico detrás del concepto, será bastante fácil implementar la solución que se adapte a su problema. Hay muchas buenas fuentes que describen el concepto. Lo que me "salvó" hace años sobre este problema en particular fue la sección 2.3 en "El arte de la programación informática".

U. Hjort
fuente