Ich habe ein unsortiertes Array . Ich habe Abfragen, in denen ich einen Bereich gebe und dann der Maximalwert aus diesem Bereich zurückgegeben werden muss. Beispielsweise:
array[]={23,17,9,45,78,2,4,6,90,1};
query(both inclusive): 2 6
answer: 78
Welchen Algorithmus oder welche Datenstruktur konstruiere ich, um schnell den Maximalwert aus einem beliebigen Bereich abzurufen? (Es gibt viele Fragen)
EDIT: Dies ist in der Tat eine einfache Version des eigentlichen Problems. Ich kann eine Arraygröße von bis zu 100000 und eine Anzahl von Abfragen von bis zu 100000 haben. Daher benötige ich definitiv eine Vorverarbeitung, die eine schnelle Antwort auf Abfragen ermöglicht.
algorithms
array
sudeepdino008
quelle
quelle
Antworten:
Ich denke, Sie könnten eine Art Binärbaum erstellen, in dem jeder Knoten den Maximalwert seiner untergeordneten Knoten darstellt:
Dann müssen Sie nur noch einen Weg finden, um zu bestimmen, welche Knoten Sie minimal überprüfen müssen, um den Maximalwert in dem abgefragten Bereich zu finden. In diesem Beispiel
[2, 6]
hätten Siemax(45, 78, 4)
stattdessen den Maximalwert im Indexbereich (einschließlich)max(9, 45, 78, 2, 4)
. Wenn der Baum wächst, ist der Gewinn größer.quelle
78
(und das überspringen2
) angezeigt werden müssen , da sich der Index nach allem, was sie weiß,6
in diesem Teilbaum befindet.Zur Ergänzung der Antwort von ngoaho91.
Der beste Weg, um dieses Problem zu lösen, ist die Verwendung der Segmentbaum-Datenstruktur. Auf diese Weise können Sie solche Abfragen in O (log (n)) beantworten. Dies bedeutet, dass die Gesamtkomplexität Ihres Algorithmus O (Q logn) ist, wobei Q die Anzahl der Abfragen ist. Wenn Sie den naiven Algorithmus verwenden würden, wäre die Gesamtkomplexität O (Q n), was offensichtlich langsamer ist.
Es gibt jedoch einen Nachteil bei der Verwendung von Segmentbäumen. Es nimmt viel Speicher in Anspruch, aber oft interessiert Sie weniger das Gedächtnis als die Geschwindigkeit.
Ich werde kurz die von diesem DS verwendeten Algorithmen beschreiben:
Der Segmentbaum ist nur ein Sonderfall eines binären Suchbaums, bei dem jeder Knoten den Wert des Bereichs enthält, dem er zugewiesen ist. Dem Wurzelknoten wird der Bereich [0, n] zugewiesen. Dem linken Kind wird der Bereich [0, (0 + n) / 2] und dem rechten Kind [(0 + n) / 2 + 1, n] zugewiesen. Auf diese Weise wird der Baum gebaut.
Baum erstellen :
Abfragebaum
Wenn Sie weitere Erklärungen benötigen, lassen Sie es mich einfach wissen.
Übrigens unterstützt der Segmentbaum auch die Aktualisierung eines einzelnen Elements oder einer Reihe von Elementen in O (log n).
quelle
O(log(n))
bis jedes Element zum Baum hinzugefügt wird. Daher ist die GesamtkomplexitätO(nlog(n))
Der beste Algorithmus wäre in O (n) -Zeit wie unten. Lassen Sie Start, Ende der Index der Bereichsgrenzen sein
quelle
max
zua[i]
und starten Sie diefor
Schleife ani+1
.)start
, anhalten beiend
). Und ich stimme zu, dies ist das Beste für eine einmalige Suche. Die Antwort von @ ThijsvanDien ist nur dann besser, wenn die Suche mehrmals durchgeführt wird, da die anfängliche Einrichtung länger dauert.Die auf binären Bäumen / Segmentbäumen basierenden Lösungen zeigen tatsächlich in die richtige Richtung. Man könnte jedoch einwenden, dass sie viel zusätzlichen Speicher benötigen. Für diese Probleme gibt es zwei Lösungen:
Der erste Punkt ist, dass Sie, da der Baum stark strukturiert ist, eine Heap-ähnliche Struktur verwenden können, um den Baum implizit zu definieren, anstatt den Baum mit Knoten, linken und rechten Zeigern, Intervallen usw. darzustellen. Dies spart im Wesentlichen viel Speicher Kein Leistungstreffer - Sie müssen etwas mehr Zeigerarithmetik ausführen.
Der zweite Punkt ist, dass Sie auf Kosten von etwas mehr Arbeit während der Auswertung einen M-ary-Baum anstelle eines binären Baums verwenden können. Wenn Sie beispielsweise einen 3-Ary-Baum verwenden, berechnen Sie maximal 3 Elemente gleichzeitig, dann 9 Elemente gleichzeitig, dann 27 usw. Der zusätzliche Speicherbedarf beträgt dann N / (M-1) - Sie können beweisen mit der geometrischen Reihenformel. Wenn Sie beispielsweise M = 11 wählen, benötigen Sie 1/10 der Speicherung der Binärbaummethode.
Sie können überprüfen, ob diese naiven und optimierten Implementierungen in Python dieselben Ergebnisse liefern:
vs.
quelle
Versuchen Sie "Segmentbaum" Datenstruktur
gibt es 2 Schritte
build_tree () O (n)
Abfrage (int min, int max) O (nlogn)
http://en.wikipedia.org/wiki/Segment_tree
bearbeiten:
Ihr lest einfach nicht das Wiki, das ich gesendet habe!
Dieser Algorithmus lautet:
- Sie durchlaufen das Array 1 Mal, um einen Baum zu erstellen. O (n)
- Wenn Sie das nächste Mal 100000000+ Mal wissen möchten, wie viele Teile eines Arrays maximal sind, rufen Sie einfach die Abfragefunktion auf. O (logn) für jede Abfrage
- c ++ hier implementieren geeksforgeeks.org/segment-tree-set-1-range-minimum-query/
alter Algorithmus ist:
jede Abfrage, einfach den ausgewählten Bereich durchlaufen und suchen.
Wenn Sie diesen Algorithmus also verwenden, um ihn einmal zu verarbeiten, ist er langsamer als bisher. Wenn Sie jedoch eine große Anzahl von Abfragen (Milliarden) verarbeiten
möchten, ist es sehr effizient, eine Textdatei wie diese für die Testzeile 1: 50000 Zufallszahl von 0-1000000 zu generieren, geteilt durch die
Zeile '(Leerzeichen)' (es ist das Array) 2: 2 Zufallszahl von 1 bis 50000, geteilt durch '(Leerzeichen)' (es ist die Abfrage)
...
Zeile 200000: mag Zeile 2, es ist auch eine zufällige Abfrage
Dies ist das Beispielproblem, sorry, aber dies ist auf vietnamesisch
http://vn.spoj.com/problems/NKLINEUP/.
Wenn Sie es auf alte Weise lösen, bestehen Sie nie.
quelle
O(n)
Suche im Array, wie in der Antwort von tarun_telang beschrieben. Der erste Instinkt ist, dass diesO(log n + k)
schneller ist alsO(n)
, aberO(log n + k)
nur das Sub-Array abgerufen wird - entspricht demO(1)
Array-Zugriff angesichts der Start- und Endpunkte. Sie müssten es immer noch durchlaufen, um das Maximum zu finden.Sie können O (1) pro Abfrage (mit O (n log n) -Konstruktion) mithilfe der Datenstruktur erreichen, die als Sparse-Tabelle bezeichnet wird. Für jede Potenz von 2 sparen wir maximal für jedes Segment dieser Länge. Wenn Sie nun das Segment [l, r) angeben, erhalten Sie maximal [l + 2 ^ k) und [r-2 ^ k, r) für das entsprechende k. Sie überlappen sich, aber es ist in Ordnung
quelle