Mencari struktur data Python Tree yang bagus [tertutup]

92

Saya mencari kelas struktur data Pohon yang bagus. Saya telah menemukan paket ini , tetapi karena saya relatif baru mengenal Python (bukan pemrograman), saya tidak tahu apakah ada yang lebih baik di luar sana.

Saya ingin mendengar dari Pythonistas di sini - apakah Anda memiliki skrip pohon favorit yang biasa Anda gunakan dan akan rekomendasikan?

[Sunting]

Untuk memperjelas, dengan 'Pohon', yang saya maksud adalah pohon tak beraturan sederhana (Hmm, itu sedikit definisi rekursif - tapi mudah-mudahan, itu menjelaskan banyak hal). Mengenai apa yang saya butuhkan pohon itu untuk (contoh kasus penggunaan). Saya membaca data pohon dari file datar dan saya perlu membangun pohon dari data dan melintasi semua node di pohon.

morf
sumber
1
Sementara itu, ada struktur data pohon yang sederhana, ringan, dan dapat diperluas: anytree.readthedocs.io/en/latest
c0fec0de

Jawaban:

38

Gulung milik Anda sendiri. Misalnya, cukup buat model pohon Anda sebagai daftar daftar. Anda harus merinci kebutuhan spesifik Anda sebelum orang dapat memberikan rekomendasi yang lebih baik.

Menanggapi pertanyaan HelloGoodbye, ini adalah kode contoh untuk iterasi pohon.

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

Salah satu tangkapannya adalah implementasi rekursif ini adalah O (n log n). Ini berfungsi dengan baik untuk semua pohon yang harus saya tangani. Mungkin subgenerator di Python 3 akan membantu.

Wai Yip Tung
sumber
Bagaimana Anda mengulang semua elemen di pohon seperti itu dengan cara 'pythonic'?
HelloGoodbye
Biasanya Anda melakukan iterasi pohon menggunakan DFS atau BFS. Saya biasanya menerapkan generator menggunakan DFS seperti def walk (pohon): ...
Wai Yip Tung
1
Apa itu DFS dan BFS? Akronim ini baru bagi saya.
HelloGoodbye
1
Menambahkan kode sampel untuk DFS.
Wai Yip Tung
1
Pencarian kedalaman-pertama berarti bahwa anak-anak dari sebuah node dikunjungi sebelum saudara kandungnya. jadi jika Anda memiliki `[A, [B, [C, D]], [E, [F, G]]]`, maka, dengan asumsi Anda mengunjungi B sebelum E, Anda juga mengunjungi C dan D sebelum E. Luas- pencarian pertama berarti semua node pada level yang sama dikunjungi sebelum ada anak mereka, jadi B dan E keduanya akan dikunjungi sebelum C, D, F, atau G.
Mark Reed
76

Anda dapat membangun pohon penis yang bagus seperti ini:

import collections

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

Ini mungkin tidak persis seperti yang Anda inginkan tetapi ini cukup berguna! Nilai disimpan hanya di simpul daun. Berikut adalah contoh cara kerjanya:

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

Untuk informasi lebih lanjut, lihat intinya .

Stefan
sumber
1
Wow, menggunakan defaultdict adalah ide yang brilian!
laike9m
Great and I wall selalu menggunakan try kecuali dengan setter.
Jimmy Kane
5
satu kelemahannya adalah sangat sulit untuk menambahkan metode yang berhubungan dengan manipulasi pohon. juga ini ada di wiki dan disebut autovivification: en.wikipedia.org/wiki/Autovivification#Python
denfromufa
41

Saya menemukan modul yang ditulis oleh Brett Alistair Kromkamp yang belum selesai. Saya menyelesaikannya dan mempublikasikannya di github dan menamainya sebagai treelib(aslipyTree ):

https://github.com/caesar0301/treelib

Semoga itu membantu Anda ....

caesar0301
sumber
5
lisensi adalah GPL, sayang
denfromufa
12
Lisensi ini diberikan ketika saya bahkan tidak menyadari apa arti lisensi. Saya tahu itu modul yang sederhana tapi berguna. Sejak versi 1.3.0, saya mendistribusikannya di bawah Lisensi Apache. Sekarang Anda dapat menggunakannya di mana Anda membutuhkannya dengan deklamasi hak cipta asli.
caesar0301
9

Membangun jawaban yang diberikan di atas dengan Pohon baris tunggal menggunakan defaultdict , Anda dapat menjadikannya sebuah kelas. Ini akan memungkinkan Anda untuk mengatur default dalam konstruktor dan membangunnya dengan cara lain.

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

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

Contoh ini memungkinkan Anda membuat referensi kembali sehingga setiap node dapat merujuk ke induknya di pohon.

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

Selanjutnya, Anda bahkan dapat mengganti __setattr__ pada pohon kelas sehingga saat menugaskan ulang induk, itu menghapusnya sebagai anak dari induk itu. Banyak hal keren dengan pola ini.

Sandy Chapman
sumber
Induk dipecah untuk t [0] [1] [2] pada contoh di atas. AttributeError: objek 'int' tidak memiliki atribut 'induk'
MatthieuBizien
@oao Ini tidak rusak. Anda menentukan t [0] [1] [2] = 3. Oleh karena itu t [0] [1] [2] tidak akan menjadi tipe defaultdict, tetapi tipe Number (karena defaultdict digunakan untuk membuat default untuk elemen yang hilang). Jika Anda ingin menjadi defaultdict, Anda perlu menggunakan t [0] [1] [2] tanpa penetapan.
Sandy Chapman
7

Untuk pohon dengan anak-anak yang teratur, saya biasanya melakukan sesuatu seperti ini (meskipun sedikit kurang umum, disesuaikan dengan apa yang saya lakukan):

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)

Anda dapat melakukan sesuatu yang sebanding dengan a dictatau menggunakan DictMixinatau itu keturunan yang lebih modern jika Anda ingin anak-anak yang tidak berurutan diakses dengan kunci.

Matt Anderson
sumber
7

Mungkin ada baiknya menulis pembungkus pohon Anda sendiri berdasarkan grafik berarah asiklik menggunakan pustaka networkx .

Andrew Walker
sumber
4

Ini sesuatu yang sedang saya kerjakan.

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)

Gunakan seperti itu (angka digunakan sebagai nilai contoh): t = Tree(1, Tree(2, Tree(4)), Tree(3, Tree(5)))

Aaron Robson
sumber
3

Akankah BTrees membantu? Mereka adalah bagian dari kode Database Objek Zope. Mengunduh seluruh paket ZODB agak berlebihan, tapi saya harap modul BTrees setidaknya bisa dipisahkan.

Jenn D.
sumber
2

Saya rasa, dari pengalaman saya sendiri tentang masalah dengan struktur data yang lebih canggih, hal terpenting yang dapat Anda lakukan di sini adalah mendapatkan pengetahuan yang baik tentang konsep umum pohon sebagai struktur data. Jika Anda memahami mekanisme dasar di balik konsep tersebut, maka akan cukup mudah untuk menerapkan solusi yang sesuai dengan masalah Anda. Ada banyak sumber bagus di luar sana yang menjelaskan konsep tersebut. Apa yang "menyelamatkan" saya bertahun-tahun yang lalu tentang masalah khusus ini adalah bagian 2.3 dalam "Seni Pemrograman Komputer".

U. Hjort
sumber