Dies ist eine Hausaufgabenfrage. Sie sagen, es dauert O(logN + logM)
wo N
und M
sind die Arrays Längen.
Nennen wir die Arrays a
und b
. Offensichtlich können wir alle ignorieren a[i]
und b[i]
wo i> k.
Vergleichen wir zuerst a[k/2]
und b[k/2]
. Lassen Sie b[k/2]
> a[k/2]
. Daher können wir auch alle verwerfen b[i]
, wobei i> k / 2 ist.
Jetzt haben wir alle a[i]
, wo i <k und alle b[i]
, wo i <k / 2, um die Antwort zu finden.
Was ist der nächste Schritt?
arrays
algorithm
binary-search
Michael
quelle
quelle
O(logN + logM)
nur auf die Zeit, die benötigt wird, um das k-te Element zu finden? Kann die Gewerkschaft vorher vorverarbeitet werden?Antworten:
Du hast es, mach einfach weiter! Und seien Sie vorsichtig mit den Indizes ...
Zur Vereinfachung gehe ich davon aus, dass N und M> k sind, daher ist die Komplexität hier O (log k), also O (log N + log M).
Pseudocode:
Für die Demonstration können Sie die Schleifeninvariante i + j = k verwenden, aber ich werde nicht alle Ihre Hausaufgaben machen :)
quelle
Ich hoffe, ich beantworte Ihre Hausaufgaben nicht, da diese Frage vor über einem Jahr gestellt wurde. Hier ist eine rekursive Endlösung, die log (len (a) + len (b)) Zeit benötigt.
Annahme: Die Eingänge sind richtig. dh k liegt im Bereich [0, len (a) + len (b)]
Basisfälle:
Reduktionsschritte:
a
+ mittlerer Index vonb
kleiner als istk
a
größer als das mittlere Element von istb
, können wir die erste Hälfte von ignorierenb
und anpassenk
.a
, passen Sie ank
.k
kleiner als die Summe der Mittelindizes vona
undb
:a
größer als das mittlere Element von istb
, können wir die zweite Hälfte von ignorierena
b
Code:
Bitte beachten Sie, dass meine Lösung darin besteht, bei jedem Aufruf neue Kopien kleinerer Arrays zu erstellen. Dies kann leicht beseitigt werden, indem nur Start- und Endindizes für die ursprünglichen Arrays übergeben werden.
quelle
kthlargest()
es gibt(k+1)
-th kleinste Elemente zurück, z. B.1
ist es das zweitkleinste Element in0,1,2,3
dh deine Funktion gibt zurücksorted(a+b)[k]
.Viele Leute beantworteten diese Frage "kth kleinstes Element aus zwei sortierten Arrays", aber normalerweise nur mit allgemeinen Ideen, nicht mit einem klaren Arbeitscode oder einer Randbedingungsanalyse.
Hier möchte ich es sorgfältig ausarbeiten, um einigen Anfängern das Verständnis mit meinem korrekt funktionierenden Java-Code zu erleichtern.
A1
undA2
sind zwei sortierte aufsteigende Arrays mitsize1
bzw.size2
als Länge. Wir müssen das k-te kleinste Element aus der Vereinigung dieser beiden Arrays finden. Hier nehmen wir das vernünftigerweise an(k > 0 && k <= size1 + size2)
, was dies impliziertA1
undA2
nicht beide leer sein kann.Lassen Sie uns diese Frage zunächst mit einem langsamen O (k) -Algorithmus angehen. Die Methode besteht darin, das erste Element beider Arrays
A1[0]
und zu vergleichenA2[0]
. Nehmen Sie den kleineren, sagen SieA1[0]
weg in unsere Tasche. Dann vergleicheA1[1]
mitA2[0]
und so weiter. Wiederholen Sie diesen Vorgang, bis unsere Taschek
Elemente erreicht hat . Sehr wichtig: Im ersten Schritt können wir uns nurA1[0]
in unserer Tasche festlegen. Wir können NICHT einschließen oder ausschließenA2[0]
!!!Der folgende O (k) -Code gibt Ihnen ein Element vor der richtigen Antwort. Hier zeige ich damit meine Idee und analysiere die Randbedingungen. Ich habe den richtigen Code nach diesem:
Die mächtigste Idee ist, dass wir in jeder Schleife immer den Basisfallansatz verwenden. Nachdem wir uns auf das aktuell kleinste Element festgelegt haben, kommen wir dem Ziel einen Schritt näher: dem k-ten kleinsten Element. Springe niemals in die Mitte und mache dich verwirrt und verloren!
Den obigen Code Basisfall Durch die Beobachtung
k == 1, k == size1+size2
, und damit kombinierenA1
undA2
können nicht beide leer sein. Wir können die Logik in einen prägnanteren Stil umwandeln.Hier ist ein langsamer, aber korrekter Arbeitscode:
Jetzt können wir versuchen, einen schnelleren Algorithmus bei O (log k) auszuführen. Vergleichen Sie in ähnlicher Weise
A1[k/2]
mitA2[k/2]
; WennA1[k/2]
es kleiner ist, sollten alle Elemente vonA1[0]
bisA1[k/2]
in unserer Tasche sein. Die Idee ist, nicht nur ein Element in jeder Schleife festzulegen. Der erste Schritt enthältk/2
Elemente. Auch hier können wir nicht einschließen oder ausschließenA2[0]
zuA2[k/2]
sowieso. Im ersten Schritt können wir also nicht mehr alsk/2
Elemente verwenden. Für den zweiten Schritt können wir nicht mehr alsk/4
Elemente gehen ...Nach jedem Schritt kommen wir dem k-ten Element viel näher. Gleichzeitig wird jeder Schritt immer kleiner, bis wir erreichen
(step == 1)
, was ist(k-1 == index1+index2)
. Dann können wir uns wieder auf den einfachen und leistungsstarken Basisfall beziehen.Hier ist der korrekte Arbeitscode:
Einige Leute könnten sich Sorgen machen, wenn sie
(index1+index2)
über k-1 springen? Könnten wir den Basisfall verpassen(k-1 == index1+index2)
? Das ist nicht möglich. Sie können 0,5 + 0,25 + 0,125 addieren ... und Sie werden niemals über 1 hinausgehen.Natürlich ist es sehr einfach, den obigen Code in einen rekursiven Algorithmus umzuwandeln:
Ich hoffe, die obige Analyse und der Java-Code könnten Ihnen beim Verständnis helfen. Aber kopiere niemals meinen Code als deine Hausaufgabe! Prost ;)
quelle
else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0)
statt seinelse if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)
? (In kthSmallestSlowWithFault-Code)Hier ist eine iterative C ++ - Version der Lösung von @ lambdapilgrim (siehe Erläuterung des dortigen Algorithmus):
Es funktioniert für alle
0 <= n < (size(a) + size(b))
Indizes und istO(log(size(a)) + log(size(b)))
komplex.Beispiel
quelle
Mein Versuch für die ersten k Zahlen, die k-te Zahl in 2 sortierten Arrays und in n sortierten Arrays:
Den vollständigen Code mit Debug-Utils finden Sie unter: https://github.com/brainclone/teasers/tree/master/kth
quelle
Hier ist mein Code, der auf Jules Olleons Lösung basiert:
quelle
Hier ist meine Implementierung in C, Sie können sich für den Algorithmus auf die Erklärungen von @Jules Olléon beziehen: Die Idee hinter dem Algorithmus ist, dass wir i + j = k beibehalten und solche i und j finden, so dass a [i-1] <b [j-1] <a [i] (oder umgekehrt). Da es nun i Elemente in 'a' gibt, die kleiner als b [j-1] sind, und j-1 Elemente in 'b', die kleiner als b [j-1] sind, ist b [j-1] das i + j-1 + 1 = kth kleinstes Element. Um ein solches i, j zu finden, führt der Algorithmus eine dichotomische Suche in den Arrays durch.
quelle
Hier ist meine Lösung. Der C ++ - Code gibt den k-ten kleinsten Wert sowie die Anzahl der Iterationen aus, um den k-ten kleinsten Wert mithilfe einer Schleife zu erhalten, die meiner Meinung nach in der Reihenfolge log (k) liegt. Der Code erfordert jedoch, dass k kleiner als die Länge des ersten Arrays ist, was eine Einschränkung darstellt.
quelle
Der oben angegebene erste Pseudocode funktioniert für viele Werte nicht. Hier sind zum Beispiel zwei Arrays. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};
Es funktionierte nicht für k = 3 und k = 9 darin. Ich habe eine andere Lösung. Es ist unten angegeben.
Aber ... es funktioniert auch nicht für k = 5. Es gibt diesen geraden / ungeraden Fang von k, der es nicht einfach sein lässt.
quelle
quelle
Hier ist meine Lösung in Java. Ich werde versuchen, es weiter zu optimieren
Dies ist inspiriert von Algo bei einem wunderbaren Youtube-Video
quelle
Link zur Codekomplexität (log (n) + log (m))
Link zum Code (log (n) * log (m))
Implementierung der Lösung (log (n) + log (m))
Ich möchte dem Problem meine Erklärung hinzufügen. Dies ist ein klassisches Problem, bei dem wir die Tatsache nutzen müssen, dass die beiden Arrays sortiert sind. Wir haben zwei sortierte Arrays arr1 der Größe sz1 und arr2 der Größe sz2 erhalten
a) Nehmen wir an, wenn
Überprüfen, ob k gültig ist
dann können wir das k-te kleinste Element in der Vereinigung beider sortierter Arrays nicht finden. Geben Sie also ungültige Daten zurück. b) Wenn nun die obige Bedingung falsch ist und wir einen gültigen und realisierbaren Wert von k haben,
Edge Cases verwalten
Wir werden sowohl die Arrays durch -Infinity-Werte an der Vorderseite als auch + Infinity-Werte am Ende anhängen, um die Randfälle von k = 1,2 und k = (sz1 + sz2-1), (sz1 + sz2) usw. abzudecken.
Hauptalgorithmus
Jetzt führen wir eine binäre Suche für arr1 durch. Wir führen eine binäre Suche für arr1 durch und suchen nach einem Index i, startIndex <= i <= endIndex
so dass, wenn wir den entsprechenden Index j in arr2 unter Verwendung der Bedingung {(i + j) = k} finden, dann wenn
Da wir wissen, dass das k-te kleinste Element (k-1) Elemente hat, die kleiner sind als es in Vereinigung beider Arrays ryt? So,
In Fall 1 haben wir sichergestellt, dass arr1 [i] insgesamt (k-1) kleinere Elemente enthält, da Elemente, die kleiner als arr1 [i] im Array arr1 sind, eine Anzahl von i-1 haben, als wir wissen (arr2 [ j-1] <arr1 [i] <arr2 [j]) und die Anzahl der Elemente, die in arr2 kleiner als arr1 [i] sind, ist j-1, da j unter Verwendung von (i-1) + (j-1) = (k) gefunden wird -1) Das k-te kleinste Element ist also arr1 [i]
Die Antwort kommt jedoch möglicherweise nicht immer vom ersten Array, dh arr1, und wir haben nach Fall2 gesucht, der ähnlich wie Fall 1 erfüllt, weil (i-1) + (j-1) = (k-1). Wenn wir nun (arr1 [i-1] <arr2 [j] <arr1 [i]) haben, haben wir insgesamt k-1 Elemente, die kleiner als arr2 [j] sind, in Vereinigung beider Arrays, so dass es das k-te kleinste Element ist.
In case3 , es zu einem Fall zu bilden , 1 oder Fall 2, müssen wir i inkrementieren und j wird entsprechend ermittelt werden constraint mit {(i + j) = k} dh in Binärsuche Bewegung zu rechten Teil dh machen start = middleIndex
In case4 , es zu einem Fall zu bilden , 1 oder Fall 2, müssen wir i dekrementieren und j wird entsprechend ermittelt wird constraint mit {(i + j) = k} dh in binärer Suche nach links bewegt Teil dh machen endIndex = middleIndex .
Nun, wie man StartIndex und EndIndex zu Beginn der binären Suche über arr1 mit startindex = 1 und endIndex = ?? entscheidet. Wir müssen uns entscheiden.
Denn wenn k größer als die Größe des ersten Arrays ist, müssen wir möglicherweise eine binäre Suche über das gesamte Array arr1 durchführen, andernfalls müssen wir nur die ersten k Elemente davon nehmen, da sz1-k-Elemente niemals zur Berechnung des k-ten kleinsten beitragen können.
CODE unten gezeigt
Zur Lösung der Komplexität (log (n) * log (m))
Ich habe nur den Vorteil der Tatsache verpasst, dass für jedes i das j unter Verwendung der Einschränkung {(i-1) + (j-1) = (k-1)} gefunden werden kann. Also wurde für jedes ii die binäre Suche auf das zweite Array weiter angewendet um j so zu finden, dass arr2 [j] <= arr1 [i]. Diese Lösung kann also weiter optimiert werden
quelle
Grundsätzlich können Sie über diesen Ansatz bei jedem Schritt k / 2 Elemente verwerfen. Das K ändert sich rekursiv von k => k / 2 => k / 4 => ... bis es 1 erreicht. Die Zeitkomplexität ist also O (logk)
Bei k = 1 erhalten wir das niedrigste der beiden Arrays.
Der folgende Code befindet sich in JAVA. Bitte beachten Sie, dass wir 1 (-1) im Code von den Indizes subtrahieren, da der Index des Java-Arrays bei 0 beginnt und nicht bei 1, z. k = 3 wird durch das Element im 2. Index eines Arrays dargestellt.
quelle
Zeitkomplexität ist O (log (min (n, m)))
quelle
Die meisten Antworten, die ich hier gefunden habe, konzentrieren sich auf beide Arrays. Es ist zwar gut, aber schwieriger zu implementieren, da wir uns um viele Randfälle kümmern müssen. Außerdem sind die meisten Implementierungen rekursiv, was die räumliche Komplexität des Rekursionsstapels erhöht. Anstatt mich auf beide Arrays zu konzentrieren, habe ich mich entschieden, mich nur auf das kleinere Array zu konzentrieren und die binäre Suche nur auf das kleinere Array durchzuführen und den Zeiger für das zweite Array basierend auf dem Wert des Zeigers im ersten Array anzupassen. Durch die folgende Implementierung haben wir die Komplexität von
O(log(min(n,m))
mitO(1)
Raumkomplexität.Wir haben einen Bereich
[low, high]
für Arraya
und wir schränken diesen Bereich ein, wenn wir den Algorithmus weiter durchlaufen.sizeA
Zeigt an, wie viele Elemente vonk
Elementen aus dem Array stammen,a
und leitet sie vom Wert vonlow
und abhigh
.sizeB
ist die gleiche Definition, außer dass wir den Wert so berechnen, dasssizeA+sizeB=k
. Die basierend auf den Werten an diesen beiden Rändern schließen daraus, dass wir uns im Array nach rechts erstreckena
oder nach links schrumpfen müssen. Wenn wir an derselben Position bleiben, bedeutet dies, dass wir die Lösung gefunden haben und das Maximum der Werte an der Position vonsizeA-1
vona
undsizeB-1
von zurückgebenb
.quelle
Überprüfen Sie diesen Code.
quelle
Unterhalb des C # -Codes finden Sie das k-te kleinste Element in der Union zweier sortierter Arrays. Zeitkomplexität: O (logk)
quelle
midA
vonendA
ifk < n
.return B[startB + k - 1];