Hier ist eine einfache O(N)
Lösung, die O(N)
Platz benötigt. Ich gehe davon aus, dass wir die Eingabeliste auf nicht negative Zahlen beschränken und die erste nicht negative Zahl finden möchten, die nicht in der Liste enthalten ist.
- Finden Sie die Länge der Liste; Sagen wir es ist
N
.
- Ordnen Sie ein Array von
N
Booleschen Werten zu, die für alle initialisiert sind false
.
- Wenn für jede Zahl
X
in der Liste X
kleiner als ist N
, setzen Sie das X'th
Element des Arrays auf true
.
- Scannen Sie das Array ausgehend vom Index
0
und suchen Sie nach dem ersten Element false
. Wenn Sie den ersten false
am Index finden I
, I
ist die Antwort. Andernfalls (dh wenn alle Elemente vorhanden sind true
) lautet die Antwort N
.
In der Praxis würde das "Array von N
Booleschen Werten" wahrscheinlich als "Bitmap" oder "Bitset" codiert, das als byte
oder int
Array dargestellt wird. Dies benötigt normalerweise weniger Speicherplatz (abhängig von der Programmiersprache) und ermöglicht false
eine schnellere Suche nach dem ersten .
So / warum funktioniert der Algorithmus.
Angenommen, die N
Zahlen in der Liste sind nicht unterschiedlich oder eine oder mehrere von ihnen sind größer als N
. Dies bedeutet, dass mindestens eine Nummer in dem Bereich vorhanden sein muss 0 .. N - 1
, der nicht in der Liste enthalten ist. Das Problem, die kleinste fehlende Zahl zu finden, muss sich daher auf das Problem reduzieren, die kleinste fehlende Zahl kleiner als zu findenN
. Dies bedeutet, dass wir keine Zahlen verfolgen müssen, die größer oder gleich sind N
... weil sie nicht die Antwort sind.
Die Alternative zum vorherigen Absatz besteht darin, dass die Liste eine Permutation der Zahlen aus ist 0 .. N - 1
. In diesem Fall setzt Schritt 3 alle Elemente des Arrays auf true
und Schritt 4 sagt uns, dass die erste "fehlende" Nummer ist N
.
Die rechnerische Komplexität des Algorithmus ist O(N)
mit einer relativ kleinen Proportionalitätskonstante verbunden. Es werden zwei lineare Durchgänge durch die Liste ausgeführt oder nur ein Durchgang, wenn bekannt ist, dass die Listenlänge damit beginnt. Es ist nicht erforderlich, das Halten der gesamten Liste im Speicher darzustellen, daher ist die asymptotische Speichernutzung des Algorithmus genau das, was zur Darstellung des Arrays von Booleschen Werten erforderlich ist. dh O(N)
Bits.
(Im Gegensatz dazu setzen Algorithmen, die auf In-Memory-Sortierung oder -Partitionierung basieren, voraus, dass Sie die gesamte Liste im Speicher darstellen können. In der Form, in der die Frage gestellt wurde, wären dafür O(N)
64-Bit-Wörter erforderlich .)
@Jorn kommentiert, dass die Schritte 1 bis 3 eine Variation der Zählsortierung sind. In gewissem Sinne hat er Recht, aber die Unterschiede sind signifikant:
- Eine Zählsortierung erfordert ein Array von (mindestens)
Xmax - Xmin
Zählern, wobei Xmax
die größte Zahl in der Liste und Xmin
die kleinste Zahl in der Liste ist. Jeder Zähler muss in der Lage sein, N Zustände darzustellen; dh unter der Annahme einer binären Darstellung muss sie (mindestens) ganzzahlige ceiling(log2(N))
Bits haben.
- Um die Arraygröße zu bestimmen, muss eine Zählsortierung einen ersten Durchgang durch die Liste machen, um
Xmax
und zu bestimmen Xmin
.
- Der minimale Platzbedarf im ungünstigsten Fall beträgt daher
ceiling(log2(N)) * (Xmax - Xmin)
Bits.
Im Gegensatz dazu erfordert der oben vorgestellte Algorithmus N
im schlimmsten und besten Fall einfach Bits.
Diese Analyse führt jedoch zu der Intuition, dass der Algorithmus, wenn er die Liste zunächst nach einer Null durchsucht (und bei Bedarf die Listenelemente zählt), eine schnellere Antwort ohne Leerzeichen geben würde, wenn er die Null findet. Es lohnt sich auf jeden Fall, dies zu tun, wenn eine hohe Wahrscheinlichkeit besteht, mindestens eine Null in der Liste zu finden. Und dieser zusätzliche Durchgang ändert nichts an der Gesamtkomplexität.
BEARBEITEN: Ich habe die Beschreibung des Algorithmus geändert, um "Array von Booleschen Werten" zu verwenden, da die Leute meine ursprüngliche Beschreibung mit Bits und Bitmaps anscheinend als verwirrend empfanden.