Ich muss das k-te kleinste Element im binären Suchbaum finden, ohne eine statische / globale Variable zu verwenden. Wie kann man es effizient erreichen? Die Lösung, die ich im Kopf habe, ist die Operation in O (n), der schlimmste Fall, da ich vorhabe, den gesamten Baum in der Reihenfolge zu durchlaufen. Aber tief im Inneren habe ich das Gefühl, dass ich die BST-Eigenschaft hier nicht benutze. Ist meine angenommene Lösung korrekt oder gibt es eine bessere?
112
Antworten:
Hier ist nur ein Überblick über die Idee:
In einer BST
T
enthält der linke Teilbaum des Knotens nur Elemente, die kleiner als der darin gespeicherte Wert sindT
. Wennk
es kleiner als die Anzahl der Elemente im linken Teilbaum ist, muss dask
kleinste Element zum linken Teilbaum gehören. Wennk
es größer ist, befindet sich dask
kleinste Element im rechten Teilbaum.Wir können die BST erweitern, damit jeder Knoten die Anzahl der Elemente in seinem linken Teilbaum speichert (vorausgesetzt, der linke Teilbaum eines bestimmten Knotens enthält diesen Knoten). Mit dieser Information ist es einfach, den Baum zu durchlaufen, indem wiederholt nach der Anzahl der Elemente im linken Teilbaum gefragt wird, um zu entscheiden, ob in den linken oder rechten Teilbaum zurückgegriffen werden soll.
Nehmen wir nun an, wir befinden uns am Knoten T:
T
.k
kleinste sind. Wir reduzieren das Problem also darauf, dask - num_elements(left subtree of T)
kleinste Element des richtigen Teilbaums zu finden.k
kleinste Teil irgendwo im linken Teilbaum, sodass wir das Problem darauf reduzieren, dask
kleinste Element im linken Teilbaum zu finden.Komplexitätsanalyse:
Dies braucht
O(depth of node)
Zeit, wasO(log n)
im schlimmsten Fall bei einer ausgeglichenen BST oderO(log n)
im Durchschnitt bei einer zufälligen BST der Fall ist .Ein BST erfordert
O(n)
Speicherplatz, und ein anderer benötigtO(n)
zum Speichern der Informationen über die Anzahl der Elemente. Alle BST-Operationen benötigenO(depth of node)
Zeit, und es dauertO(depth of node)
zusätzliche Zeit, um die Informationen zur "Anzahl der Elemente" zum Einfügen, Löschen oder Drehen von Knoten zu verwalten. Durch das Speichern von Informationen über die Anzahl der Elemente im linken Teilbaum bleibt daher die räumliche und zeitliche Komplexität einer BST erhalten.quelle
Eine einfachere Lösung wäre, eine Inorder Traversal durchzuführen und das aktuell zu druckende Element zu verfolgen (ohne es zu drucken). Wenn wir k erreichen, drucken Sie das Element und überspringen Sie den Rest der Baumdurchquerung.
quelle
Dies ist meine Implementierung in C #, basierend auf dem obigen Algorithmus. Ich dachte nur, ich würde es veröffentlichen, damit die Leute besser verstehen können, dass es für mich funktioniert
Danke IVlad
quelle
Eine einfachere Lösung wäre, eine Inorder Traversal durchzuführen und das aktuell zu druckende Element mit einem Zähler k zu verfolgen. Wenn wir k erreichen, drucken Sie das Element. Die Laufzeit ist O (n). Denken Sie daran, dass der Funktionstyp nicht ungültig sein kann. Er muss nach jedem rekursiven Aufruf seinen aktualisierten Wert von k zurückgeben. Eine bessere Lösung hierfür wäre eine erweiterte BST mit einem sortierten Positionswert an jedem Knoten.
quelle
// eine Java-Version ohne Rekursion hinzufügen
quelle
Sie können die iterative Inorder Traversal verwenden: http://en.wikipedia.org/wiki/Tree_traversal#Iterative_Traversal mit einer einfachen Überprüfung des k-ten Elements, nachdem Sie einen Knoten aus dem Stapel entfernt haben.
quelle
Wenn Sie nur einen einfachen binären Suchbaum haben, können Sie nur mit dem kleinsten beginnen und nach oben gehen, um den richtigen Knoten zu finden.
Wenn Sie dies sehr oft tun, können Sie jedem Knoten ein Attribut hinzufügen, das angibt, wie viele Knoten sich in seinem linken Unterbaum befinden. Auf diese Weise können Sie den Baum direkt zum richtigen Knoten absteigen.
quelle
Rekursiver In-Order-Walk mit einem Zähler
Die Idee ähnelt der @ prasadvk-Lösung, weist jedoch einige Mängel auf (siehe Anmerkungen unten). Daher veröffentliche ich diese als separate Antwort.
Anmerkungen (und Unterschiede zur Lösung von @ prasadvk):
if( counter == k )
Der Test ist an drei Stellen erforderlich : (a) nach dem linken Teilbaum, (b) nach der Wurzel und (c) nach dem rechten Teilbaum. Dies soll sicherstellen, dass das k-te Element für alle Standorte erkannt wird , dh unabhängig von dem Teilbaum, in dem es sich befindet.if( result == -1 )
Test erforderlich, um sicherzustellen, dass nur das Ergebniselement gedruckt wird. Andernfalls werden alle Elemente vom k-ten kleinsten bis zur Wurzel gedruckt.quelle
O(k + d)
, wod
die maximale Tiefe des Baums ist. Daher wird eine globale Variable verwendetcounter
, die für diese Frage jedoch unzulässig ist.Für einen nicht ausgeglichenen Suchbaum wird O (n) benötigt .
Für einen ausgeglichenen Suchbaum wird im schlimmsten Fall O (k + log n ) benötigt, im amortisierten Sinne jedoch nur O (k) .
Die zusätzliche Ganzzahl für jeden Knoten haben und verwalten: Die Größe des Unterbaums gibt O (log n) Zeitkomplexität. Ein solcher ausgeglichener Suchbaum wird normalerweise als RankTree bezeichnet.
Im Allgemeinen gibt es Lösungen (basierend nicht auf Baum).
Grüße.
quelle
Dies funktioniert gut: status: ist das Array, das enthält, ob ein Element gefunden wird. k: ist das zu findende k-te Element. count: Verfolgt die Anzahl der Knoten, die während der Baumdurchquerung durchlaufen wurden.
quelle
Dies ist definitiv nicht die optimale Lösung für das Problem, aber es ist eine weitere mögliche Lösung, die einige Leute für interessant halten könnten:
quelle
Unterschrift:
Anruf als:
Definition:
quelle
Na hier sind meine 2 Cent ...
quelle
Das ist was ich aber und es funktioniert. Es wird in o (log n) ausgeführt.
quelle
Nun, wir können einfach die Durchlaufreihenfolge verwenden und das besuchte Element auf einen Stapel schieben. pop k wie oft, um die Antwort zu bekommen.
Wir können auch nach k Elementen anhalten
quelle
Lösung für den vollständigen BST-Fall: -
quelle
Der Linux-Kernel verfügt über eine hervorragende erweiterte Rot-Schwarz-Baum-Datenstruktur, die rangbasierte Operationen in O (log n) in linux / lib / rbtree.c unterstützt.
Einen sehr groben Java-Port finden Sie auch unter http://code.google.com/p/refolding/source/browse/trunk/core/src/main/java/it/unibo/refolding/alg/RbTree.java , zusammen mit RbRoot.java und RbNode.java. Das n-te Element kann durch Aufrufen von RbNode.nth (RbNode-Knoten, int n) erhalten werden, wobei die Wurzel des Baums übergeben wird.
quelle
Hier ist eine kurze Version in C # , die das k-te kleinste Element zurückgibt , jedoch die Übergabe von k als ref-Argument erfordert (es ist der gleiche Ansatz wie bei @prasadvk):
Es ist O (log n), um den kleinsten Knoten zu finden , und dann O (k), um zum k-ten Knoten zu gelangen, also ist es O (k + log n).
quelle
http://www.geeksforgeeks.org/archives/10379
Dies ist die genaue Antwort auf diese Frage:
1. Verwenden der Inorder Traversal zur O (n) -Zeit 2. Verwenden des Augmented Tree in der Zeit k + log n
quelle
Ich konnte keinen besseren Algorithmus finden. Also habe ich beschlossen, einen zu schreiben. Korrigieren Sie mich, wenn dies falsch ist.
}}
quelle
Hier ist der Java-Code,
max (Knotenwurzel, int k) - um den k-ten größten zu finden
min (Node root, int k) - um kth Smallest zu finden
quelle
das würde auch funktionieren. Rufen Sie einfach die Funktion mit maxNode im Baum auf
def k_largest (self, node, k): wenn k <0: return None
wenn k == 0: return node else: k - = 1 return self.k_largest (self.predecessor (node), k)
quelle
Ich denke, dies ist besser als die akzeptierte Antwort, da der ursprüngliche Baumknoten nicht geändert werden muss, um die Anzahl seiner untergeordneten Knoten zu speichern.
Wir müssen nur die In-Order-Traversal verwenden, um den kleinsten Knoten von links nach rechts zu zählen. Stoppen Sie die Suche, sobald die Anzahl gleich K ist.
quelle
Der beste Ansatz ist bereits vorhanden. Aber ich möchte einen einfachen Code dafür hinzufügen
}}
quelle
Verwenden der Hilfsergebnisklasse, um zu verfolgen, ob ein Knoten gefunden wurde und aktuell k.
quelle
Python-Lösung Zeitkomplexität: O (n) Raumkomplexität: O (1)
Die Idee ist, Morris Inorder Traversal zu verwenden
quelle
Ich habe eine nette Funktion geschrieben, um das k-te kleinste Element zu berechnen. Ich verwende die Durchquerung in der richtigen Reihenfolge und halte an, wenn das k-te kleinste Element erreicht ist.
quelle
quelle
quelle
}}
quelle