Tree: Knoten einsetzen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hallo miteinander

Ich habe eine Tree-Klasse und möchte da gerne die Funktion "add" hinzufügen.
Momentan sieht diese so aus:

Code: Alles auswählen

def add(self,root,left,right):
        if(root == None):
            root = root
        else:
            if not (left == None):
                self.left = self.add(self.left, left, None)
            if not (right == None):
                self.right = self.add(self.right, None, right)
        return root
Momentan gebe ich nur die roots zurück, weil ich denke, dass bei den children noch nicht alles stimmt :(
Und zugegebenermassen: ich weiss nicht genau, was. Daher bitte ich euch hier um Hilfe, um mir einen kleinen Denkanstoss zu geben.
Vielen Dank dafür!
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Zuerst: Vergleiche mit None macht man mit `is` bzw `is not` und die ganzen Klammern koennen da auch weg.

Erklaer doch mal was diese Methode machen soll und warum man immer gleich 3 Argumente uebergeben soll.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Ok, danke für den ersten Tipp.

Also, angenommen, man hat einen Baum mit "Max" als root:
tree = Tree("Max")

Dann möchte man via insert "Fabienne" und "Petra" als children hinzufügen:
tree.insert("Max", "Fabienne", "Petra")

Es müssen immer 3 Argumente sein, um erstens den jeweiligen parent anzugeben und zweitens wollen wir nur vollständige Bäume, d.h. falls nur ein linkes oder ein rechtes Kind definiert wird, soll nichts gemacht werden.
Wie könnte man das am besten implementieren?
BlackJack

@MarcelF6: Also ich habe immer noch nicht verstanden was die drei Argumente sollen. Wozu das erste Argument? Soll danach erst im Baum gesucht werden? Wo ist dann die Definition der Suchoperation? Und muss man da dann wirklich alle Knoten nach durchsuchen, bzw. mindestens die Blätter? Wäre es dann nicht sinnvoll eine Baumstruktur zu haben die sich die Blätter merkt, damit man nicht tatsächlich den ganzen Baum durchsuchen muss? Und wozu wird die Datenstruktur am Ende verwendet?
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Das erste Argument ist nötig, damit man weiss, wo die neuen children hinzugefügt werden müssen.
Suchfunktion habe ich im Moment diese hier:

Code: Alles auswählen

    def traversal(self):
        if self == None: return
        self.left.traversal()
        self.right.traversal()
        print self.root_value,
Allerdings weiss ich (noch) nicht, ob sie wie gewünscht funktioniert.

Wegen der Baumstruktur...es ist halt so vorgegeben, dass z.B. folgender Aufruf möglich sein muss:
test = Tree("Max")
test.insert("Max","Petra","Leo")
test.insert("Petra","Laura","David")

Danke für jede Hilfe!
BlackJack

@MarcelF6: Das ist keine Suchfunktion, denn der müsste man ja übergeben *was* man sucht. Und wenn Du beim Einfügen angeben können sollst welchen Wert der Knoten haben soll bei dem eingefügt wird, dann brauchst Du ganz offensichtlich *erst* eine *funktionierende* Suchfunktion.

Edit: In Lisp-Syntax verkleidet könnte das so aussehen. :-)

Code: Alles auswählen

#!/usr/bin/env hy
(import [itertools [chain]])


(defclass Tree [object]

  (defn --init-- [self value &optional left right]
    (when (= 1 (+ (none? left) (none? right)))
      (raise (ValueError "left and right must both be given or None")))
    (setv self.value value
          self.left left
          self.right left))

  (defn --repr-- [self]
    (.format "(Tree {0.value!r} {0.left!r} {0.right!r})" self))

  (defn leaf? [self] (and (none? self.left) (none? self.right)))

  (defn iter-nodes [self]
    (chain
      [self]
      (if (none? self.left) [] (.iter-nodes self.left))
      (if (none? self.right) [] (.iter-nodes self.right))))

  (defn find? [self predicate]
    (let [result None]
      (for [node (.iter-nodes self)]
        (when (predicate node)
          (setv result node)
          (break))
        (else (raise (ValueError "no matching node found"))))
      result))

  (defn insert [self value left-child-value right-child-value]
    (let [node (.find? self (fn [n] (and (= n.value value) (.leaf? n))))]
      (setv node.left (Tree left-child-value)
            node.right (Tree right-child-value)))))


(defmain [&rest args]
  (print
    (doto (Tree "Max")
      (.insert "Max" "Petra" "Leo")
      (.insert "Petra" "Laura" "David"))))
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hallo BlackJack,
ich habe zurzeit folgende Klasse definiert:

Code: Alles auswählen

class Tree:
    def __init__(self, root_value, left=None, right=None):
        if left is None and right is None:
            pass
        self.root_value = root_value
        self.left = left
        self.right = right

    def __str__(self):
        return str(self.root_value)

    def size(self):
        count = 1
        if self.left:
            count += self.left.size()
        if self.right:
            count += self.right.size()
        return count

    def depth(self):
        left_depth = self.left.depth() if self.left else 0
        right_depth = self.right.depth() if self.right else 0
        return max(left_depth, right_depth) + 1

    def traversal_help(self):
        if self.left is not None:
            for node in self.left.traversal_help():
                yield node
        yield self
        if self.right is not None:
            for node in self.right.traversal_help():
                yield node

    def traversal(self):
        out = []
        for el in list(self.traversal_help()):
            out.append(str(el))
        return out

    def insert(self,parent,left,right):
        for node in self.traversal_help():
            if node.root_value == parent and node.right == None and node.left == None:
                node.left = Tree(left)
                node.right = Tree(right)
                return
(Mindestens) mit der Funktion "insert" kann etwas nicht stimmen, denn wenn ich meine Klasse via online-Auswerter teste, erhalte ich die Fehlermeldung:
" File "<string>", line 4, in <module>
AttributeError: 'NoneType' object has no attribute 'root_value'"

Leider kann man nicht sehen, mit welchen Eingaben der online-Auswerter testet, aber gibt es eine bessere Darstellung / Implementierung der insert-Funktion?
Für die Funktion "insert" muss gelten: es wird der parent gesucht, und dort left und right unterhalb eingesetzt. Dabei müssen zwingend left UND right vorhanden sein, andernfalls sollte nichts unternommen werden. Zudem ist es möglich, dass bereits existierende children überschrieben werden.

Danke für jeden Tipp und Hinweis! :)

PS: Natürlich dürfen auch andere Funktionen umgeschrieben werden, solange sie das machen, was sie sollten ;)
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Ich habe in der Zwischenzeit die insert-Methode etwas verändert:

Code: Alles auswählen

def insert(self,parent=None,left=None,right=None):
        if left is not None and right is not None and parent is not None:
            for node in self.traversal():
                if node == parent:
                    self.left = Tree(left)
                    self.right = Tree(right)
                    return
Allerdings ist sie immernoch nicht gut; der Fehler taucht leider immernoch auf :(
Hat jemand einen anderen Vorschlag für die insert-Methode? (Vielleicht ohne die traversal-Methode?)

Danke für die Hilfe!
BlackJack

@MarcelF6: Warum denn ohne `traversal()`? Da anscheinend keine Suchbaumeigenschaften von der Datenstruktur erfüllt werden muss man alle Knoten durchgehen und wenn man dafür bereits eine Methode hat, macht es Sinn die zu verwenden.

Ich habe so ein bisschen das Gefühl Du probierst hier nur noch herum statt geziehlt eine Lösung zu programmieren.

Warum sind alle Argumente von `insert()` optional obwohl für einen sinnvollen Einsatz eigentlich keines der Argumente optional ist? Warum macht die Methode einfach gar nichts wenn eines der Argumente `None` ist? So ein Aufruf wäre nicht sinnvoll und im Grunde ein Programmierfehler. Fehler sollte man nicht ignorieren sondern eine entsprechende Ausnahme auslösen.

Was sind denn `parent`, `left`, und `right` für Werte? Was liefert `traversal()` für Werte über die iteriert wird? Was wird in Zeile 4 miteinander verglichen? Macht das Sinn?
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich denke das Grundproblem ist, dass Du einen Baum mit nur einer Klasse aufbauen möchtest. Es wird leichter wenn Du es mit ca. 3 Klassen versuchst: Tree, Branch, Leaf.
"ca." weil es auf den Anwendungsfall darauf ankommt, ob Dir die Trennung von Branch und Leaf was nützt und ob Du Branch und Leaf von einer Klasse Node ableiten solltest.
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: Ich denke nicht, dass *das* hier ein Grundproblem ist. Die Aufgabe lässt sich problemlos und einfach mit einer einzigen Klasse lösen, das habe ich in Hy weiter oben ja schon gemacht. Verschiedene Typen machen Sinn wenn die entweder deutlich unterschiedliche Daten enthalten oder sich deutlich unterschiedlich verhalten. Beides ist hier nicht gegeben.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hey BlackJack,

du hast zum Teil recht, dass ich eher probiere als eine gezielte Lösung zu programmieren...das liegt vor allem auch daran, dass ich den "Fehler" nicht wirklich nachvollziehen kann. Bei mir funktionierte der Code bisher mit jeder Eingabe....

"Warum sind alle Argumente von `insert()` optional obwohl für einen sinnvollen Einsatz eigentlich keines der Argumente optional ist?"
--> Stimmt, das ist so nicht sinnvoll.
"Warum macht die Methode einfach gar nichts wenn eines der Argumente `None` ist?"
--> Die Methode soll nichts machen, wenn left oder right None ist (das ist so vorgegeben).
"Was sind denn `parent`, `left`, und `right` für Werte?"
--> "parent" ist der Knoten, an dem "left" und "right" angehängt werden sollen. Es handelt sich dabei immer um strings.

Wegen der Zeile 4: Da wollte ich eben prüfen, ob der "Elternknoten" wirklich == parent ist...
BlackJack

@MarcelF6: Was ist `parent` bitte? Ein Knoten oder eine Zeichenkette? Laut Deiner Beispielaufrufe eine Zeichenkette. Und was ist `node`? Eine Zeichenkette? Oder ein Knoten? Du solltest Dir da vielleicht mal *ausgeben* lassen an was die beiden Namen vor dem Vergleich gebunden sind.

Das ist entweder nicht der Quelltext den Du tatsächlich verwendest oder Deine Testeingaben sind nicht sinnvoll gewählt, denn das kann so ganz offensichtlich nicht funktionieren.
Antworten