Kürzlich hatte ich ein Interview, in dem sie mir eine " Suchfrage " stellten.
Die Frage war:
Angenommen, es gibt ein Array von (positiven) Ganzzahlen, von denen jedes Element entweder ist
+1
oder-1
mit seinen benachbarten Elementen verglichen wird.Beispiel:
array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
Suchen Sie nun nach
7
seiner Position und geben Sie sie zurück.
Ich gab diese Antwort:
Speichern Sie die Werte in einem temporären Array, sortieren Sie sie und wenden Sie dann die binäre Suche an.
Wenn das Element gefunden wird, geben Sie seine Position im temporären Array zurück.
(Wenn die Zahl zweimal vorkommt, geben Sie ihr erstes Vorkommen zurück.)
Aber sie schienen mit dieser Antwort nicht zufrieden zu sein.
Was ist die richtige Antwort?
Antworten:
Sie können eine lineare Suche mit Schritten durchführen, die häufig größer als 1 sind. Die entscheidende Beobachtung ist, dass, wenn z. B.
array[i] == 4
und 7 noch nicht erschienen sind, der nächste Kandidat für 7 im Index isti+3
. Verwenden Sie eine while-Schleife, die wiederholt direkt zum nächsten geeigneten Kandidaten führt.Hier ist eine leicht verallgemeinerte Implementierung. Es findet das erste Auftreten
k
im Array (vorbehaltlich der Einschränkung + = 1) oder-1
wenn es nicht auftritt:Ausgabe:
quelle
O(N)
, aber ich glaube nicht, dass es einen schnelleren Weg gibt, dies zu tun.Ihr Ansatz ist zu kompliziert. Sie müssen nicht jedes Array-Element untersuchen. Der erste Wert ist
4
, also7
sind zumindest7-4
Elemente entfernt, und Sie können diese überspringen.Programmausgabe:
Bearbeiten: verbessert nach Kommentaren von @Raphael Miedl und @Martin Zabel.
quelle
if ((skip = 7 - array[i]) < 1) skip = 1;
scheint es meiner Meinung nach zu komplizieren und zu pessimieren. Wennarray[i] == 200
Sie-193
jedes Mal um 1 kommen und einfach überspringen, obwohl Sie alle 193 überspringen könnten. Warum nicht einfachi += abs(7 - array[i])
?skip
die absolute Differenz zwischen 7 und einstellenarray[i]
.200
, du hättest bestanden7
.+1
/-1
voneinander sind. So könnte es einfach seinarray[0] == 200
und die anderen sind meistens-1
.Eine Variation der herkömmlichen linearen Suche könnte ein guter Weg sein. Lassen Sie uns ein Element auswählen, sagen wir
array[i] = 2
. Jetzt istarray[i + 1]
entweder 1 oder 3 (ungerade),array[i + 2]
ist (nur positive ganze Zahlen) 2 oder 4 (gerade Zahl).Wenn Sie so weitermachen, ist ein Muster zu beobachten -
array[i + 2*n]
es enthält gerade Zahlen, sodass alle diese Indizes ignoriert werden können.Das können wir auch sehen
Daher
i + 5
sollte der Index als nächstes überprüft werden, und eine while-Schleife kann verwendet werden, um den nächsten zu überprüfenden Index zu bestimmen, abhängig von dem am Index gefundenen Werti + 5
.Dies hat zwar Komplexität
O(n)
(lineare Zeit in Bezug auf asymptotische Komplexität), ist jedoch in praktischer Hinsicht besser als eine normale lineare Suche, da nicht alle Indizes besucht werden.Offensichtlich wird all dies umgekehrt, wenn
array[i]
(unser Ausgangspunkt) ungerade war.quelle
Der von John Coleman vorgestellte Ansatz ist höchstwahrscheinlich der, auf den der Interviewer gehofft hat.
Wenn Sie bereit sind, etwas komplizierter zu werden, können Sie die erwartete Sprunglänge erhöhen:
Rufen Sie den Zielwert k auf . Beginnen Sie mit dem Wert v des ersten Elements an Position p und nennen Sie die Differenz kv dv mit dem Absolutwert av . Um negative Suchvorgänge zu beschleunigen, werfen Sie einen Blick auf das letzte Element als den anderen Wert u an Position o: Wenn dv × du negativ ist, ist k vorhanden (wenn ein Auftreten von k akzeptabel ist, können Sie den Indexbereich hier wie bei der binären Suche eingrenzen). Wenn av + au größer als die Länge des Arrays ist, fehlt k. (Wenn dv × du Null ist, ist v oder u gleich k.)
Auslassen der Indexgültigkeit: Prüfen Sie die ("nächste") Position, an der die Sequenz mit k in der Mitte zu v zurückkehren könnte :
o = p + 2*av
.Wenn dv × du negativ ist, finde k (rekursiv?) Von p + av bis o-au;
Wenn es Null ist, ist u gleich k bei o.
Wenn ich gleich dv und der Wert in der Mitte ist nicht k oder au überschreitet av,
oder Sie scheitern k von p zu finden + av o-au,
lassen
p=o; dv=du; av=au;
und halten Sondieren.(Für einen vollständigen Rückblick auf die Texte der 60er Jahre mit Courier anzeigen. Mein "1. 2. Gedanke" war zu verwenden
o = p + 2*av - 1
, was du gleich dv ausschließt .)quelle
SCHRITT 1
Beginnen Sie mit dem ersten Element und prüfen Sie, ob es 7 ist. Angenommen, es
c
handelt sich um den Index der aktuellen Position. Also zunächstc = 0
.SCHRITT 2
Wenn es 7 ist, haben Sie den Index gefunden. Es ist
c
. Wenn Sie das Ende des Arrays erreicht haben, brechen Sie aus.SCHRITT 3
Wenn dies nicht der Fall ist, müssen 7 mindestens
|array[c]-7|
Positionen entfernt sein, da Sie nur eine Einheit pro Index hinzufügen können. Fügen|array[c]-7|
Sie daher zu Ihrem aktuellen Index c hinzu, und gehen Sie erneut zu SCHRITT 2, um dies zu überprüfen.Im schlimmsten Fall, wenn es abwechselnd 1 und -1 gibt, kann die Zeitkomplexität O (n) erreichen, aber durchschnittliche Fälle würden schnell geliefert.
quelle
|c-7|
wo dies|array[c]-7|
array[c]-7
kann also positiv oder negativ sein. Sie müssen sichabs()
vor dem Vorwärtsspringen darauf bewerben .array[c] - 7
mit dem Modul-Operator|array[c] - 7|
.Hier gebe ich die Implementierung in Java ...
quelle
Hier ist eine Lösung im Divide-and-Conquer-Stil. Auf Kosten von (viel) mehr Buchhaltung können wir mehr Elemente überspringen; Anstatt von links nach rechts zu scannen, testen Sie in der Mitte und überspringen Sie in beide Richtungen.
quelle
Wollte eine rekursive Lösung für das Problem enthalten. Genießen
quelle