Alla ricerca di una buona struttura dati ad albero Python [chiuso]


92

Sto cercando una buona classe di struttura dati ad albero. Mi sono imbattuto in questo pacchetto , ma dal momento che sono relativamente nuovo in Python (non programmando), non so se ce ne siano di migliori là fuori.

Mi piacerebbe sentire i Pythonistas qui: hai uno script albero preferito che usi regolarmente e che consiglieresti?

[Modificare]

Per chiarire, con "Albero", intendo un semplice albero non ordinato (Hmm, questa è una definizione un po 'ricorsiva - ma si spera, questo chiarisce un po' le cose). Riguardo a cosa mi serve l'albero (cioè caso d'uso). Sto leggendo i dati dell'albero da un file flat e ho bisogno di creare un albero dai dati e attraversare tutti i nodi dell'albero.



1
Nel frattempo, esiste una struttura dati ad albero semplice, leggera ed estensibile: anytree.readthedocs.io/en/latest
c0fec0de

Risposte:


38

Rotola il tuo. Ad esempio, modella il tuo albero come elenco di elenchi. Dovresti dettagliare le tue esigenze specifiche prima che le persone possano fornire consigli migliori.

In risposta alla domanda di HelloGoodbye, questo è un codice di esempio per iterare un albero.

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 è che questa implementazione ricorsiva è O (n log n). Funziona bene per tutti gli alberi con cui ho a che fare. Forse il sottogeneratore in Python 3 potrebbe aiutare.


Come fai a ripetere tutti gli elementi in un albero del genere in modo "pitonico"?
Ciao Arrivederci

In genere si itera un albero utilizzando DFS o BFS. Di solito implemento un generatore usando DFS come def walk (albero): ...
Wai Yip Tung,

1
Cosa sono DFS e BFS? Questi acronimi sono nuovi per me.
HelloGoodbye

1
Aggiunto un codice di esempio per DFS.
Wai Yip Tung

1
La ricerca in profondità significa che i figli di un nodo vengono visitati prima dei suoi fratelli. quindi se hai "[A, [B, [C, D]], [E, [F, G]]]", quindi, supponendo che visiti B prima di E, visiti anche C e D prima di E. Larghezza- la prima ricerca significa che tutti i nodi allo stesso livello vengono visitati prima dei loro figli, quindi B ed E sarebbero entrambi visitati prima di C, D, F o G.
Mark Reed

76

Puoi costruire un bell'albero di dicts of dict come questo:

import collections

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

Potrebbe non essere esattamente quello che vuoi ma è abbastanza utile! I valori vengono salvati solo nei nodi foglia. Ecco un esempio di come funziona:

>>> 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'})}) 

Per ulteriori informazioni dai un'occhiata all'essenza .


1
Wow, usare defaultdict è un'idea geniale!
laike9m

Ottimo e cerco sempre di usare il try tranne che con i setter.
Jimmy Kane

5
uno svantaggio è che è molto complicato aggiungere metodi relativi alla manipolazione degli alberi. anche questo è in wiki e chiamato autovivificazione: en.wikipedia.org/wiki/Autovivification#Python
denfromufa

41

Ho trovato un modulo scritto da Brett Alistair Kromkamp che non è stato completato. L'ho finito e l'ho reso pubblico su GitHub e l'ho rinominato come treelib(originale pyTree):

https://github.com/caesar0301/treelib

Possa aiutarti ...


5
la licenza è GPL, che peccato
denfromufa

12
Questa licenza è stata data quando non ero nemmeno a conoscenza del significato di una licenza. So che è un modulo semplice ma utile. Dalla versione 1.3.0, l'ho ridistribuito con licenza Apache. Ora puoi usarlo dove ti serve con la declamazione del copyright originale.
caesar0301

9

Basandosi sulla risposta data sopra con l'albero a riga singola usando defaultdict , puoi renderlo una classe. Ciò ti consentirà di impostare le impostazioni predefinite in un costruttore e di costruirle in altri modi.

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

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

Questo esempio consente di fare un riferimento all'indietro in modo che ogni nodo possa fare riferimento al suo genitore nell'albero.

>>> 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

Successivamente, potresti persino sovrascrivere __setattr__ sulla classe Tree in modo che quando riassegni il genitore, lo rimuove come figlio da quel genitore. Molte cose interessanti con questo modello.


Il genitore è interrotto per t [0] [1] [2] nell'esempio precedente. AttributeError: l'oggetto 'int' non ha attributo 'genitore'
MatthieuBizien

@oao Questo non è rotto. Stai specificando t [0] [1] [2] = 3. Quindi t [0] [1] [2] non sarà un tipo di defaultdict, ma un tipo di numero (come defaultdict è usato per fare i valori di default per elementi mancanti). Se vuoi che sia un defaultdict, devi usare t [0] [1] [2] senza l'assegnazione.
Sandy Chapman

7

Per un albero con bambini ordinati, di solito faccio qualcosa di simile (anche se un po 'meno generico, adattato a quello che sto facendo):

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)

Potresti fare qualcosa di paragonabile a dicto usando DictMixino è discendenti più moderni se vuoi che i bambini non ordinati accedano per chiave.


7

Potrebbe valere la pena scrivere il tuo tree wrapper basato su un grafo diretto aciclico usando la libreria networkx .


5

Un'altra implementazione buona e facile da usare degli alberi in Python è pyTree: https://github.com/caesar0301/pyTree

pyTree fornisce anche la possibilità di visualizzare l'albero:

Harry[harry]
|___ Jane[jane]
|    |___ Diane[diane]
|         |___ George[george]
|              |___ Jill[jill]
|         |___ Mary[mary]
|    |___ Mark[mark]
|___ Bill[bill]

2
Un vantaggio per aver cercato di visualizzare l'albero, anche se il rendering era orribile.
Ciao Arrivederci


4

Ecco qualcosa su cui stavo lavorando.

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)

Usa come tale (numeri usati come valori di esempio): t = Tree(1, Tree(2, Tree(4)), Tree(3, Tree(5)))


3

BTrees sarebbe d' aiuto? Fanno parte del codice Zope Object Database. Scaricare l'intero pacchetto ZODB è un po 'eccessivo, ma spero che il modulo BTrees sia almeno un po' separabile.


2

Penso, in base alla mia esperienza su problemi con strutture dati più avanzate, che la cosa più importante che puoi fare qui è acquisire una buona conoscenza del concetto generale di tress come strutture dati. Se comprendi il meccanismo di base alla base del concetto, sarà abbastanza facile implementare la soluzione che si adatta al tuo problema. Ci sono molte buone fonti là fuori che descrivono il concetto. Ciò che mi ha "salvato" anni fa su questo particolare problema è stata la sezione 2.3 in "The Art of Computer Programming".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.