Szukam dobrej struktury danych Python Tree [zamknięte]

92

Szukam dobrej klasy struktury danych Tree. Natknąłem się na ten pakiet , ale ponieważ jestem stosunkowo nowy w Pythonie (nie programowaniu), nie wiem, czy są tam lepsze.

Chciałbym usłyszeć od Pythonistas tutaj - czy masz ulubiony skrypt drzewa, z którego regularnie korzystasz i który poleciłbyś?

[Edytować]

Aby wyjaśnić, przez „Drzewo” mam na myśli proste, nieuporządkowane drzewo (Hmm, to trochę rekurencyjna definicja - ale miejmy nadzieję, że to trochę wyjaśnia). Jeśli chodzi o to, do czego potrzebuję drzewa (np. Przypadek użycia). Czytam dane drzewa z płaskiego pliku i muszę zbudować drzewo z danych i przejść przez wszystkie węzły w drzewie.

morfeusz
źródło
Co najmniej potrójny duplikat ... stackoverflow.com/questions/2482602/ ... a także stackoverflow.com/questions/2358045/ ...
eric
1
W międzyczasie istnieje prosta, lekka i rozszerzalna struktura danych drzewa: anytree.readthedocs.io/en/latest
c0fec0de

Odpowiedzi:

38

Skręć swój własny. Na przykład po prostu zamodeluj swoje drzewo jako listę list. Powinieneś szczegółowo opisać swoją konkretną potrzebę, zanim ludzie będą mogli przedstawić lepsze rekomendacje.

W odpowiedzi na pytanie HelloGoodbye, oto przykładowy kod do iteracji drzewa.

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

Jednym z haczyków jest to, że rekurencyjna implementacja to O (n log n). Działa dobrze na wszystkich drzewach, z którymi mam do czynienia. Może pomógłby subgenerator w Pythonie 3.

Wai Yip Tung
źródło
Jak zapętlić wszystkie elementy w takim drzewie w „pythonowy” sposób?
HelloGoodbye
Zazwyczaj iterujesz drzewo przy użyciu DFS lub BFS. Zwykle implementuję generator używając DFS jak def walk (tree): ...
Wai Yip Tung
1
Co to jest DFS i BFS? Te akronimy są dla mnie nowe.
HelloGoodbye
1
Dodano przykładowy kod dla DFS.
Wai Yip Tung
1
Przeszukiwanie w głąb oznacza, że ​​dzieci węzła są odwiedzane przed jego rodzeństwem. więc jeśli masz `[A, [B, [C, D]], [E, [F, G]]]`, to zakładając, że odwiedzasz B przed E, odwiedzasz również C i D przed E. Breadth- pierwsze wyszukiwanie oznacza, że ​​wszystkie węzły na tym samym poziomie są odwiedzane przed którymkolwiek z ich dzieci, więc B i E będą odwiedzane przed którymkolwiek z C, D, F lub G.
Mark Reed
76

Możesz zbudować ładne drzewo dykt w ten sposób:

import collections

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

Może to nie być dokładnie to, czego chcesz, ale jest całkiem przydatne! Wartości są zapisywane tylko w węzłach liści. Oto przykład, jak to działa:

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

Aby uzyskać więcej informacji, spójrz na sedno .

Stefan
źródło
1
Wow, użycie defaultdict to genialny pomysł!
laike9m
Świetnie i zawsze używam try, z wyjątkiem seterów.
Jimmy Kane,
5
jedną wadą jest to, że bardzo trudno jest dodać metody związane z manipulowaniem drzewami. również to jest na wiki i nazywa się autowifikacja: en.wikipedia.org/wiki/Autovivification#Python
denfromufa
41

Znalazłem moduł napisany przez Bretta Alistaira Kromkampa, który nie został ukończony. Skończyłem to i upubliczniłem na githubie i zmieniłem jego nazwę na treelib(oryginałpyTree ):

https://github.com/caesar0301/treelib

Niech ci to pomoże ...

caesar0301
źródło
5
licencja to GPL, a szkoda
denfromufa
12
Ta licencja została wydana, kiedy nawet nie wiedziałem, co oznacza licencja. Wiem, że to prosty, ale przydatny moduł. Od wersji 1.3.0 rozpowszechniam go na licencji Apache. Teraz możesz go używać tam, gdzie go potrzebujesz, z deklamacją oryginalnych praw autorskich.
caesar0301
9

Opierając się na odpowiedzi podanej powyżej z pojedynczym drzewem liniowym przy użyciu defaultdict , możesz uczynić z niego klasę. Umożliwi to ustawienie wartości domyślnych w konstruktorze i zbudowanie na nich w inny sposób.

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

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

Ten przykład umożliwia utworzenie odniesienia wstecznego, tak aby każdy węzeł mógł odwoływać się do swojego rodzica w drzewie.

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

Następnie możesz nawet nadpisać __setattr__ w drzewie klas, aby podczas ponownego przypisywania rodzica usuwał go jako dziecko z tego rodzica. Wiele fajnych rzeczy z tym wzorem.

Sandy Chapman
źródło
W powyższym przykładzie rodzic jest uszkodzony dla t [0] [1] [2]. AttributeError: obiekt „int” nie ma atrybutu „parent”
MatthieuBizien
@oao To nie jest zepsute. Podajesz t [0] [1] [2] = 3. Dlatego t [0] [1] [2] nie będzie typem domyślnym, ale typem liczbowym (ponieważ defaultdict służy do ustawiania wartości domyślnych dla brakujące elementy). Jeśli chcesz, aby był to domyślny słownik, musisz użyć t [0] [1] [2] bez przypisania.
Sandy Chapman
7

W przypadku drzewa z uporządkowanymi dziećmi zwykle robiłbym coś takiego (choć trochę mniej ogólne, dostosowane do tego, co robię):

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)

Możesz zrobić coś porównywalnego z a dictlub używając DictMixinlub bardziej nowoczesnych potomków, jeśli chcesz, aby nieuporządkowane dzieci miały dostęp za pomocą klucza.

Matt Anderson
źródło
7

Może warto napisać własne opakowanie drzewa oparte na acyklicznym ukierunkowanym grafie przy użyciu biblioteki networkx .

Andrew Walker
źródło
4

Oto coś, nad czym pracowałem.

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)

Użyj jako takich (liczby użyte jako wartości przykładowe): t = Tree(1, Tree(2, Tree(4)), Tree(3, Tree(5)))

Aaron Robson
źródło
3

Czy BTrees pomoże? Są częścią kodu Zope Object Database. Pobieranie całego pakietu ZODB to trochę przesada, ale mam nadzieję, że moduł BTrees byłby przynajmniej w pewnym stopniu rozdzielalny.

Jenn D.
źródło
2

Myślę, że na podstawie własnego doświadczenia z problemami z bardziej zaawansowanymi strukturami danych najważniejszą rzeczą, jaką możesz tutaj zrobić, jest zdobycie dobrej wiedzy na temat ogólnej koncepcji warkocza jako struktur danych. Jeśli rozumiesz podstawowy mechanizm kryjący się za tą koncepcją, wdrożenie rozwiązania pasującego do Twojego problemu będzie dość łatwe. Istnieje wiele dobrych źródeł opisujących tę koncepcję. To, co „uratowało” mnie lata temu w tym konkretnym problemie, to sekcja 2.3 w „Sztuce programowania komputerowego”.

U. Hjort
źródło