Was ist der schnellste Weg, um die erste (kleinste) Ganzzahl zu finden, die in einer gegebenen Liste unsortierter Ganzzahlen nicht existiert (und die größer ist als der kleinste Wert der Liste)?
Mein primitiver Ansatz besteht darin, sie zu sortieren und die Liste durchzugehen. Gibt es einen besseren Weg?
algorithms
search
list
numbers
Fabian Zeindl
quelle
quelle
If you have a question about… •algorithm and data structure concepts
es ist zum Thema IMHO.Antworten:
Angenommen, Sie meinen "Ganzzahl", wenn Sie "Zahl" sagen, können Sie einen Bitvektor der Größe 2 ^ n verwenden, wobei n die Anzahl der Elemente ist (sagen, Ihr Bereich umfasst Ganzzahlen zwischen 1 und 256, dann können Sie einen 256- bit oder 32 byte bitvector). Wenn Sie auf eine Ganzzahl in Position n Ihres Bereichs stoßen, setzen Sie das n-te Bit.
Wenn Sie die Auflistung der Ganzzahlen abgeschlossen haben, iterieren Sie über die Bits in Ihrem Bitvektor und suchen nach der Position aller Bits, die auf 0 gesetzt sind. Sie stimmen nun mit der Position n der fehlenden Ganzzahlen überein.
Dies ist O (2 * N), daher O (N) und wahrscheinlich speichereffizienter als das Sortieren der gesamten Liste.
quelle
Wenn Sie zuerst die gesamte Liste sortieren, ist die Laufzeit im ungünstigsten Fall garantiert. Auch Ihre Wahl des Sortieralgorithmus ist entscheidend.
So würde ich dieses Problem angehen:
return
: Sie haben Ihre Antwort gefunden.Hier ist eine Visualisierung einer Heap-Sortierung .
quelle
Um esoterisch und "clever" zu sein, können Sie im speziellen Fall eines Arrays mit nur einem "Loch" eine XOR-basierte Lösung ausprobieren:
Dies dauert ungefähr 2N, ähnlich wie bei der Bitvector-Lösung, erfordert jedoch weniger Speicherplatz für N> sizeof (int). Wenn das Array jedoch mehrere "Löcher" hat, ist X die XOR "Summe" aller Löcher, die sich nur schwer oder gar nicht in die tatsächlichen Lochwerte aufteilen lassen. In diesem Fall greifen Sie auf eine andere Methode zurück, z. B. die Ansätze "Pivot" oder "Bitvector" aus anderen Antworten.
Sie können dies auch mithilfe der Pivot-Methode wiederholen, um die Komplexität weiter zu verringern. Ordnen Sie das Array basierend auf einem Drehpunkt neu an (der das Maximum der linken und das Minimum der rechten Seite ist; es ist trivial, das Maximum und das Minimum des vollständigen Arrays beim Drehen zu ermitteln). Wenn die linke Seite des Zapfens ein oder mehrere Löcher hat, greifen Sie nur auf diese Seite zurück. ansonsten auf die andere Seite zurückgreifen. Verwenden Sie an jedem Punkt, an dem Sie feststellen können, dass es nur ein Loch gibt, die XOR-Methode, um es zu finden (die insgesamt günstiger sein sollte, als wenn Sie weiter bis zu einer Ansammlung von zwei Elementen mit einem bekannten Loch schwenken, für das der Basisfall gilt) der reine Pivot-Algorithmus).
quelle
Welchem Zahlenbereich werden Sie begegnen? Wenn dieser Bereich nicht sehr groß ist, können Sie dies mit zwei Scans (lineare Zeit O (n)) lösen, indem Sie ein Array mit so vielen Elementen verwenden, wie Sie Zahlen haben, und dabei Raum gegen Zeit tauschen. Sie können den Bereich mit einem weiteren Scan dynamisch ermitteln. Um den Speicherplatz zu verringern, können Sie jeder Zahl 1 Bit zuweisen, wodurch Sie 8 Speicherplätze pro Byte erhalten.
Die andere Option, die für frühe Szenarien besser geeignet ist und sich statt des Kopierens des Speichers in situ befindet, besteht darin, die Auswahlsortierung so zu ändern, dass sie vorzeitig beendet wird, wenn die in einem Scan-Durchgang gefundene Minute nicht um 1 höher ist als die zuletzt gefundene Minute.
quelle
Nein nicht wirklich. Da jede noch nicht gescannte Nummer immer eine sein kann, die ein bestimmtes "Loch" ausfüllt, können Sie nicht vermeiden, jede Nummer mindestens einmal zu scannen und sie dann mit ihren möglichen Nachbarn zu vergleichen. Sie könnten die Dinge wahrscheinlich beschleunigen, indem Sie einen binären Baum aufbauen und ihn dann von links nach rechts durchlaufen, bis ein Loch gefunden wird. Dies ist jedoch im Wesentlichen genauso komplex wie das Sortieren, da es sich um das Sortieren handelt. Und Sie werden wahrscheinlich nichts schnelleres als Timsort finden .
quelle
Die meisten Ideen hier sind nicht mehr als nur Sortieren. Die Bitvector-Version ist schlicht Bucketsort. Haufenart wurde auch erwähnt. Es läuft im Wesentlichen darauf hinaus, den richtigen Sortieralgorithmus auszuwählen, der von den zeitlichen und räumlichen Anforderungen sowie vom Umfang und der Anzahl der Elemente abhängt.
Aus meiner Sicht ist die Verwendung einer Heap-Struktur wahrscheinlich die allgemeinste Lösung (ein Heap liefert im Grunde genommen die kleinsten Elemente ohne vollständige Sortierung).
Sie können auch Ansätze analysieren, die zuerst die kleinsten Zahlen finden und dann nach jeder größeren Ganzzahl suchen. Oder Sie finden die 5 kleinsten Zahlen in der Hoffnung, dass sie eine Lücke haben.
Alle diese Algorithmen haben ihre Stärke in Abhängigkeit von den Eingabeeigenschaften und den Programmanforderungen.
quelle
Eine Lösung, die keinen zusätzlichen Speicher verwendet oder die Breite (32 Bit) von Ganzzahlen annimmt.
Finden Sie in einem Durchgang die kleinste Zahl. Nennen wir dies "min". O (n) zeitliche Komplexität.
Wählen Sie ein zufälliges Pivot-Element und erstellen Sie eine Partition im QuickSort-Stil.
Wenn der Pivot in der Position = ("Pivot" - "min") endete, dann rekursiv auf der rechten Seite der Partition, andernfalls rekursiv auf der linken Seite der Partition. Die Idee dabei ist, dass, wenn von Anfang an keine Löcher vorhanden sind, sich der Drehpunkt in der Position ("Drehpunkt" - "min") befinden würde, sodass das erste Loch rechts von der Trennwand liegen sollte und umgekehrt.
Basisfall ist ein Array von 1 Element und die Bohrung liegt zwischen diesem und dem nächsten Element.
Die erwartete Gesamtlaufzeitkomplexität ist O (n) (8 * n mit den Konstanten) und der schlechteste Fall ist O (n ^ 2). Die Zeitkomplexitätsanalyse für ein ähnliches Problem finden Sie hier .
quelle
Ich glaube, ich habe etwas gefunden, das allgemein und effizient funktionieren sollte, wenn Sie garantiert keine Duplikate haben * (es sollte jedoch auf eine beliebige Anzahl von Löchern und einen beliebigen Bereich von ganzen Zahlen erweiterbar sein).
Die Idee hinter dieser Methode ist wie bei der Quicksort-Methode, bei der wir einen Drehpunkt und eine Trennwand finden und dann auf die Seite (n) mit einem Loch zurückgreifen. Um zu sehen, welche Seiten das Loch haben, ermitteln wir die niedrigsten und höchsten Zahlen und vergleichen sie mit dem Drehpunkt und der Anzahl der Werte auf dieser Seite. Angenommen, der Drehpunkt ist 17 und die Mindestanzahl ist 11. Wenn keine Löcher vorhanden sind, sollten 6 Zahlen vorhanden sein (11, 12, 13, 14, 15, 16, 17). Wenn es 5 gibt, wissen wir, dass es auf dieser Seite ein Loch gibt, und wir können nur auf dieser Seite zurückgreifen, um es zu finden. Ich habe Probleme, es klarer zu erklären. Nehmen wir also ein Beispiel.
Drehpunkt:
15 ist der Drehpunkt, angezeigt durch Rohre (
||
). Es gibt 5 Zahlen auf der linken Seite des Pivots, wie es sein sollte (15 - 10), und 9 auf der rechten Seite, wo es 10 sein sollte (25 - 15). Wir kehren also auf die rechte Seite zurück. Wir werden bemerken, dass die vorherige Grenze 15 war, falls das Loch daneben liegt (16).Jetzt gibt es 4 Zahlen auf der linken Seite, aber es sollten 5 sein (21 - 16). Wir kehren dort also zurück und notieren erneut die vorherige Schranke (in Klammern).
Die linke Seite hat die richtigen 2 Zahlen (18 - 16), aber die rechte hat 1 anstelle von 2 (20 - 18). Abhängig von unseren Endebedingungen können wir die 1-Zahl mit den beiden Seiten (18, 20) vergleichen und feststellen, dass 19 fehlt oder noch einmal verwendet wird:
Die linke Seite hat eine Größe von Null, mit einer Lücke zwischen dem Zapfen (20) und der vorherigen Grenze (18), also ist 19 das Loch.
*: Wenn Duplikate vorhanden sind, können Sie diese möglicherweise mithilfe eines Hash-Satzes in O (N) entfernen, wobei die Gesamtmethode O (N) beibehalten wird. Dies kann jedoch mehr Zeit in Anspruch nehmen als die Verwendung einer anderen Methode.
quelle