Implementieren Sie eine queue, in die push_rear(), pop_front() und get_min() sind alle Konstanten Zeit-Operationen
Stieß ich auf diese Frage:
Implementieren Sie eine queue, in die push_rear(), pop_front() und get_min() sind alle Operationen Konstante Zeit.
Ursprünglich habe ich daran gedacht, mit einem min-heap-Datenstruktur, die O(1) Komplexität für eine get_min(). Aber push_rear() und pop_front() wäre O(log(n)).
Weiß jemand, was wäre die beste Art und Weise zu implementieren, die eine solche Warteschlange, die hat O(1) push(), pop() und min()?
Habe ich gegoogelt, und wollte darauf hinweisen, diese Algorithmus Geeks thread. Aber es scheint, dass keine der Lösungen Folgen konstanter Zeit gilt für alle 3 Methoden: push(), pop() und min().
Danke für die vielen Vorschläge.
Ich bin nicht sicher, es ist ein Google-interview-Frage, ich sah es zunächst auf careercup.com/question?id=7263132 .... Es fühlt sich an wie die Frage gemeint worst-case-Grenzen. Scheint es unmöglich?
Nein, es scheint durchaus möglich, und ich bin ankurbeln Weg jetzt. 🙂 Ich war auf der Suche bei Verwendung von kartesischen Bäume - dies gibt Ihnen die O(1) amortisiert einsetzen und O(1) lookup, und ich habe fast O(1) amortisiert Löschung so gut funktioniert. Aber, wenn Sie sind auf der Suche für den worst-case bounds, werde ich ändern mein Ansatz.
ok, jetzt sehe Kdoto die Antwort von unten her, ich bin jetzt sicher, dass worst-case-Grenzen vielleicht nicht ein mögliches Ding. Also vielleicht Google-Mitarbeiter, die müssen gesucht werden, Amortisiert O(1). EDIT: ok, als templatetypedef Zeiger in die Kommentare Kdoto Antwort, die den Nachweis nicht korrekt ist. Notiert.
Nicht so sicher sein, mein Beweis war nicht richtig. Aber ich glaube nicht, dass O(1) gefunden wurde, der für alle Vorgänge, fortgeführten oder nicht. Und ich vermute, dass es nicht möglich ist.
InformationsquelleAutor bits | 2011-01-26
Du musst angemeldet sein, um einen Kommentar abzugeben.
Implementieren Sie einen stack mit O(1) pop(), push() und get_min(): so speichern Sie den aktuellen minimum-zusammen mit jedem element. So, zum Beispiel, wird der Stapel
[4,2,5,1]
(1) wird[(4,4), (2,2), (5,2), (1,1)]
.Dann können Sie verwenden Sie zwei Stapel zu implementieren, die Warteschlange. Push-to-one-stack, pop und noch eines; wenn der zweite Stapel ist leer, während die pop, verschieben Sie alle Elemente aus dem ersten Stapel auf der zweiten.
E. g für eine
pop
Anfrage, verschieben Sie alle Elemente aus dem ersten Stapel[(4,4), (2,2), (5,2), (1,1)]
, der zweite Stapel wäre[(1,1), (5,1), (2,1), (4,1)]
. und jetzt wieder top-element aus dem zweiten Stapel.Finden das minimale element der Warteschlange, der Blick in die kleinsten zwei Elemente der einzelnen min-stacks, dann das minimum dieser beiden Werte. (Es gibt natürlich noch einige zusätzliche Logik hier ist, wenn der Stapel leer ist, aber das ist nicht allzu schwer zu umgehen).
Wird es O(1)
get_min()
undpush()
und amortisiert O(1)pop()
.Jedes element kann verschoben werden von einem stack zu einem anderen nur einmal.
Wenn Sie speichern Sie die "aktuelle minimal-zusammen mit den Elementen", und Sie pop die minimale aus der Warteschlange, wie würden Sie wissen, was das neue minimum ist, in O(1) Zeit?
Ich kann nicht verstehen, 3. Teil. Wie Ihr minimum arbeitet. Wie Sie sehen, gibt es zu viele Kommentare hier. Warum nur nicht ein kleines Beispiel, mit Ihren algorithmen die Schritte. Es wird Ihnen helfen zu verstehen, Ihre Algorithmus.
Endlich verstehe ich Ihre Lösung. Sie sollten hinzufügen, um Ihre Erklärung, dass wir Werte neu berechnen von zweiten Elementen, wenn wir bewegte Elemente von der ersten Struktur zur zweiten. Durch die Art und Weise, wie ich in meiner Antwort Es ist möglich, alle diese Operationen in o(1) und nicht in amortisiert O(1). 🙂
InformationsquelleAutor adamax
Okay - ich denke ich habe eine Antwort, die Ihnen alle diese Vorgänge in fortgeführten O(1), was bedeutet, dass jeder Betrieb kann bis zu O(n), sondern eine beliebige Folge von n Operationen in O(1) Zeit pro Betrieb.
Die Idee ist, speichern Sie Ihre Daten als Kartesische Baum. Dies ist ein binärer Baum, der die Beachtung der min-heap-Eigenschaft (jeder Knoten ist nicht größer als seine Kinder) und bestellt in einer Weise, so dass ein inorder-Traversierung der Knoten gibt Sie wieder die Knoten in der gleichen Reihenfolge in der Sie Hinzugefügt wurden. Zum Beispiel, hier ist eine kartesische Struktur für die Abfolge
2 1 4 3 5
:Ist es möglich, fügen Sie ein element in einem kartesischen Baum in O(1) amortisiert Zeit mit dem folgenden Verfahren. Auf der rechten Wirbelsäule des Baumes (der Pfad von der Wurzel bis zum am weitesten rechts liegenden Blatt, geformt durch die immer Fuß auf der rechten Seite). Beginnend beim am weitesten rechts liegenden Knoten, Scannen Sie nach oben entlang diesem Pfad, bis Sie die ersten Knoten, die kleiner als der Knoten, den Sie sind einfügen.
Ändern, dass Knoten, so dass seine Rechte Kind wird diesen neuen Knoten, dann machen Sie, dass Knoten die ehemalige Rechte Kind das linke Kind des Knoten, den Sie gerade Hinzugefügt haben. Zum Beispiel, nehmen wir an, wir wollen, legen Sie eine weitere Kopie 2 in der oben stehenden Baum. Wir Wandern rechts der Wirbelsäule vorbei an der 5 und der 3, aber nicht unterhalb der 1, weil 1 < 2. Wir ändern dann den Baum wie folgt Aussehen:
Beachten Sie, dass ein inorder traversal gibt 2 1 4 3 5 2, das ist die Reihenfolge, in der wir Hinzugefügt die Werte.
Dieser läuft in amortisiert O(1) denn wir können eine potentielle Funktion gleich der Anzahl der Knoten im rechten Wirbelsäule des Baumes. Die Reale Zeit, die erforderlich ist, um einen Knoten einzufügen ist 1 plus die Anzahl der Knoten in der Wirbelsäule, die wir betrachten (nennen dieses k). Einmal finden wir den Ort zum einfügen der Knoten, der Größe der Wirbelsäule schrumpft durch die Länge k - 1, da jeder der k Knoten, die wir besuchten, sind nicht mehr auf der rechten Seite der Wirbelsäule, und der neue Knoten wird an seine Stelle. Dies gibt einen amortized cost 1 + k + (1 - k) = 2 = O(1), für den amortisiert O(1) einfügen. Als eine andere Art des Denkens über diese, sobald ein Knoten verschoben wurde aus dem rechts der Wirbelsäule, es ist nie Teil der rechten Wirbelsäule wieder, und so werden wir nie haben, um es zu verschieben wieder. Da jeder der n Knoten kann verschoben werden, höchstens einmal, das bedeutet, dass n-Insertionen tun können, bei den meisten n bewegt, so dass die Gesamt-Laufzeit ist höchstens O(n) für ein amortisiert O(1) pro element.
Tun dequeue Schritt, wir entfernen Sie einfach die am weitesten Links stehende Knoten aus der kartesische Baum. Wenn dieser Knoten ein Blatt ist, sind wir fertig. Ansonsten kann der Knoten nur ein Kind hat (das Rechte Kind), und so ersetzen wir den Knoten mit seinem rechten Kind. Vorausgesetzt, dass wir verfolgen, wo der Knoten ganz Links ist, ist dieser Schritt dauert O(1) Zeit. Jedoch nach dem entfernen der Knoten ganz Links und ersetzen Sie es mit dem rechten Kind, könnten wir nicht wissen, wo der neue Knoten ganz Links ist. Um dies zu beheben, wir gehen einfach die Links der Wirbelsäule von dem Baum ab, an den neuen Knoten wir gerade umgezogen, um die am weitesten Links stehende Kind. Ich behaupte, dass dieser noch läuft in O(1) amortisiert Zeit. Um dies zu sehen, ich behaupte, dass ein Knoten besucht wird höchstens einmal in einer dieser Pässe zu finden, die am weitesten Links stehende Knoten. Um dies zu sehen, beachten Sie, dass sobald ein Knoten besucht wurde dieser Weg, der einzige Weg, dass wir jemals benötigen könnte, um es zu betrachten wieder sein würde, wenn es waren bewegt von einem Kind der Knoten ganz Links zu den Knoten ganz Links. Aber alle Knoten besucht sind die Eltern von der ganz Links liegenden Knoten, so kann dies nicht passieren. Folglich kann jeder Knoten besucht wird höchstens einmal während dieses Prozesses, und die pop läuft in O(1).
Können wir tun, finden-min in O(1) da die kartesischen Baum gibt uns Zugriff auf das kleinste element des Baums zum Nulltarif; es ist die Wurzel des Baumes.
Schließlich, um zu sehen, dass die Knoten wieder in der gleichen Reihenfolge, in der Sie eingefügt wurden, beachten Sie, dass eine Kartesisches Struktur immer speichert seine Elemente, so dass ein inorder-Traversierung besucht Sie in sortierter Reihenfolge. Da wir immer entfernen Sie die Knoten ganz Links bei jedem Schritt, und dies ist das erste element der inorder-Traversierung, wir haben immer die Knoten in der Reihenfolge, in der Sie eingegeben wurden.
Kurz gesagt, erhalten wir O(1) amortisiert push-und pop -, und O(1) worst-case-finden-min.
Ob ich kommen kann, mit worst-case O(1) Implementierung, ich werde auf jeden Fall posten. Dies war ein großes problem; vielen Dank für die Veröffentlichung!
Gekommen, an es zu denken, müssen Sie jeden Knoten zu speichern, die parent-Zeiger, wenn Sie wollen, um die O(1) amortisiert zu entfernen, da beim entfernen ein Blatt Sie können aktualisieren Sie den Zeiger auf den linken Knoten in der Baum im worst-case O(1).
InformationsquelleAutor templatetypedef
Ok, hier ist eine Lösung.
Zunächst müssen wir ein paar Sachen, die push_back(),push_front(),pop_back() und pop_front() 0(1). Es ist einfach zu implementieren mit array und 2 Iteratoren. Erste iterator zeigen nach vorne, die zweite hinten. Nennen wir solche Sachen deque.
Ist hier pseudo-code:
Erklärung:
Beispiel let ' s push-Nummern [12,5,10,7,11,19] und unsere MyQueue
1)drücken 12
2)drücken 5
3)drücken 10
4)drücken 7
6)drücken 11
7)drücken 19
Nun nennen wir pop_front()
wir haben
Ist das minimum 5
Nennen wir pop_front() wieder
Erklärung: pop_front entfernen 5 aus D, aber es wird pop-front-element von Min zu, weil es gleich nach D s front element (5).
Und minimum ist 7. 🙂
Oops :). Sie bekam mich. Korrigiert habe ich mit push(). Jetzt funktioniert ' s richtig, aber nicht in 0(1). Es ist ein arbeiten in amortisiert O(1) wie deiner :).
Dies sollte die richtige Lösung. Danke.
job! Kleine Korrektur, obwohl, Ihre 5<12, wenn 12 tauchte aus dem Stapel.
InformationsquelleAutor UmmaGumma
Verwenden Sie eine deque (A) zum speichern der Elemente und andere deque (B) zum speichern der minima.
Wenn x in die Warteschlange eingereiht, push_back es zu Ein und halten pop_backing B, bis der Rücken von B ist kleiner als x, dann push_back x B.
beim abarbeiten der Warteschlange Ein, pop_front Eine als Rückgabewert, und wenn es gleich an der Vorderseite des B, pop_front B als gut.
bekommen, wenn die mindestens Eine, der vorderen von B als Rückgabewert.
dequeue und getmin sind offensichtlich O(1). Für den enqueue-operation, sollten die push_back von n Elementen. Es gibt n push_back A, n push_back zu B und bei den meisten n pop_back von B, weil jedes element bleiben entweder in B oder knallte einmal aus B., Über alle gibt es O(3n) Operationen und damit die amortisierten Kosten O(1) als auch für enqueue.
Schließlich der Grund, warum dieser Algorithmus funktioniert, ist, dass, wenn Sie enqueue x zu A, wenn es Elemente in B, die größer sind als x, wird er sich nie minima jetzt, weil x bleibt in der Warteschlange länger ist als alle Elemente in B (eine queue ist FIFO). Deshalb brauchen wir, um pop-out-Elementen in B (von hinten), die größer sind als x vor drücken wir x in B.
InformationsquelleAutor jianglai
Wenn Sie nichts dagegen haben, speichern Sie ein wenig mehr Daten, sollte es trivial sein, die zum speichern der minimum Wert. Push und pop können den Wert aktualisieren, wenn die neue oder entfernte element ist das minimum, und die Rückkehr der Mindestwert ist so einfach wie immer wird der Wert der Variablen.
Dies ist unter der Annahme, dass get_min() ändert nicht die Daten; wenn Sie lieber etwas wie pop_min() (d.h. entfernen Sie das minimale element), können Sie einfach speichern Sie einen Zeiger auf das aktuelle element und das element vor es (wenn überhaupt), und aktualisieren Sie diese entsprechend mit push_rear() und pop_front ().
Bearbeiten nach Kommentare:
Offensichtlich führt dies zu O(n) push-und pop-im Falle, dass die minimalen änderungen an diesen Vorgängen, und damit streng genommen nicht den Anforderungen genügen.
Ich denke, get_min() nicht wirklich pop der Daten. Aber pop_front() wird die pop-Daten. Lets sagen, dass der vordere Knoten ist auch der min-Knoten ist, also seine knallte. Nun, wie können wir halten die min-Eigenschaft in konstanter Zeit?
Ah, gut nennen...aber du hast Recht, @bits, es ist nur O(n) in dem Fall, dass Sie drücken ein neuer minimal-oder pop-aktuelle minimum. Wenn es sein muss worst-case O(1), weiß ich nicht, dass es möglich ist, aber ich würde es lieben, anders zu sehen.
InformationsquelleAutor Andy Mikula
Lösungen zu dieser Frage, einschließlich der code, kann hier gefunden werden:
http://discuss.joelonsoftware.com/default.asp?interview.11.742223.32
InformationsquelleAutor Olhovsky
Können Sie tatsächlich nutzen eine LinkedList zur Aufrechterhaltung der Warteschlange.
Jedes element in der LinkedList vom Typ
Können Sie haben zwei Zeiger verweist auf die Start-und die anderen Punkte zu Ende.
Wenn Sie ein element an den Anfang der Warteschlange. Untersuchen Sie die Start-Zeiger und den Knoten zu legen. Wenn Knoten einfügen currentmin ist weniger als beginnen currentmin Knoten einfügen currentmin ist das minimum. Sonst update der currentmin mit start currentmin.
Wiederholen Sie das gleiche für die enque.
InformationsquelleAutor Sandeep
Dass code ist sehr selbsterklärend. Wenn Sie möchten, Erklärung, könnten Sie Fragen, anstatt nach unten die Abstimmung, bitte?
Eine der Qualitäten, die ich mag am besten über StackOverflow ist die hohe Qualität der Antworten hier. Wenn ich andere Websites besuchen, wie es scheint, jeder, der Beiträge ist nur bis zu werfen Bündel von "selbsterklärenden code", wie deiner. Zwangsläufig, manche sind falsch und jeder nimmt sich Zeit zu verstehen und zu überprüfen. Eine gute Antwort führt Sie durch den Bewerbungsprozess und präventiv Antworten auf Fragen, die Sie haben könnten. Es ist schwer sich zu erinnern, wieder zu kommen und überprüfen Sie auf diese Dinge, so dass ich lieber runter zu Stimmen und dann zu neutralisieren oder zu Stimmen.
AFAICT, dies ist der gleiche Algorithmus wie bereits schon gegeben ist, als Quell-code und beschrieben von jianglai mehr als einen Monat zuvor.
InformationsquelleAutor TheMan
Diese Lösung enthält 2 Warteschlangen:
1. main_q - speichert die eingegebenen Ziffern.
2. min_q - speichert die min zahlen wird durch gewisse Regeln, die wir beschrieben (erscheint in Funktionen MainQ.enqueue(x), MainQ.dequeue(), MainQ.get_min()).
Hier ist der code in Python. Queue implementiert mit einer Liste.
Die Grundidee liegt in der MainQ.enqueue(x), MainQ.dequeue(), MainQ.get_min () - Funktionen.
Eine Grundannahme ist, dass die Entleerung eine queue in o(0).
Ein test ist am Ende.
Den Ausgang des oben genannten test ist:
InformationsquelleAutor user3139774
Java-Implementierung
InformationsquelleAutor Nitish Bhagat
Wir wissen, dass push und pop sind Operationen Konstante Zeit (O(1) um genau zu sein].
Aber wenn wir denken, get_min()[ich.e finden Sie die aktuelle minimale Anzahl in der Warteschlange] in der Regel das erste, was mir einfällt, ist die Suche in der gesamten Warteschlange jedes mal, wenn die Anforderung für das minimale element ist gemacht. Aber diese werden niemals die ständige Zeit, die Bedienung, das ist das wichtigste Ziel, das problem.
Dies ist in der Regel gefragt, sehr Häufig in den interviews, so müssen Sie wissen, der trick
Zu diesem Zweck haben wir zwei weitere Warteschlangen, die halten die Spur, minimale element und wir haben, ändern diese 2 queues, wie wir tun, push und pop-Operationen auf der queue, so dass minimale element ist die in O(1) Zeit.
Hier ist der sich selbst beschreibenden sudo-code basierend auf dem obigen Ansatz erwähnt.
InformationsquelleAutor Sachin