Hier ist also die O (n log n) -Lösung in Java.
long merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, count = 0;
while (i < left.length || j < right.length) {
if (i == left.length) {
arr[i+j] = right[j];
j++;
} else if (j == right.length) {
arr[i+j] = left[i];
i++;
} else if (left[i] <= right[j]) {
arr[i+j] = left[i];
i++;
} else {
arr[i+j] = right[j];
count += left.length-i;
j++;
}
}
return count;
}
long invCount(int[] arr) {
if (arr.length < 2)
return 0;
int m = (arr.length + 1) / 2;
int left[] = Arrays.copyOfRange(arr, 0, m);
int right[] = Arrays.copyOfRange(arr, m, arr.length);
return invCount(left) + invCount(right) + merge(arr, left, right);
}
Dies ist eine fast normale Zusammenführungssortierung. Die gesamte Magie ist in der Zusammenführungsfunktion verborgen. Beachten Sie, dass beim Sortieren des Algorithmus Inversionen entfernt werden. Beim Zusammenführen zählt der Algorithmus die Anzahl der entfernten Inversionen (sortiert könnte man sagen).
Der einzige Moment, in dem Inversionen entfernt werden, ist, wenn der Algorithmus ein Element von der rechten Seite eines Arrays nimmt und es mit dem Hauptarray zusammenführt. Die Anzahl der durch diese Operation entfernten Inversionen ist die Anzahl der Elemente, die vom linken Array übrig bleiben, das zusammengeführt werden soll. :) :)
Hoffe es ist erklärend genug.
left.length - i
zum Inversionszähler los? Ich würde denken, es wäre sinnvoll, nur 1 hinzuzufügen, da Sie in den logischen Fall geraten sind, dass der Vergleich zwischen den beiden Subarrays ein größeres linkes Array-Element als das rechte hat. Kann es mir jemand erklären, als wäre ich 5?arr
. Aber es ist keine Umkehrung. Sie haben Inversionen für alle Elemente im linken Array gefunden, die größer als 6 sind. In unserem Fall enthält es auch 8. Also wird 2 addiertcount
, was gleich istleft.length - i
.Ich habe es in O (n * log n) Zeit durch die folgende Methode gefunden.
Nehmen Sie A [1] und finden Sie seine Position im sortierten Array B über eine binäre Suche. Die Anzahl der Inversionen für dieses Element ist eins weniger als die Indexnummer seiner Position in B, da jede niedrigere Zahl, die nach dem ersten Element von A erscheint, eine Inversion ist.
2a. Akkumulieren Sie die Anzahl der Inversionen, um der Variablen num_inversions entgegenzuwirken.
2b. Entfernen Sie A [1] aus Array A und auch aus seiner entsprechenden Position in Array B.
Hier ist ein Beispiellauf dieses Algorithmus. Ursprüngliches Array A = (6, 9, 1, 14, 8, 12, 3, 2)
1: Sortierung zusammenführen und in Array B kopieren
B = (1, 2, 3, 6, 8, 9, 12, 14)
2: Nehmen Sie A [1] und binäre Suche, um es in Array B zu finden
A [1] = 6
B = (1, 2, 3, 6 , 8, 9, 12, 14)
6 befindet sich an der 4. Position von Array B, daher gibt es 3 Inversionen. Wir wissen dies, weil 6 in Array A an erster Stelle stand, sodass jedes Element mit niedrigerem Wert, das anschließend in Array A erscheint, einen Index von j> i haben würde (da i in diesem Fall 1 ist).
2.b: Entfernen Sie A [1] aus Array A und auch aus der entsprechenden Position in Array B (fett gedruckte Elemente werden entfernt).
A = ( 6, 9, 1, 14, 8, 12, 3, 2) = (9, 1, 14, 8, 12, 3, 2)
B = (1, 2, 3, 6, 8, 9, 12, 14) = (1, 2, 3, 8, 9, 12, 14)
3: Führen Sie ab Schritt 2 die neuen A- und B-Arrays erneut aus.
A [1] = 9
B = (1, 2, 3, 8, 9, 12, 14)
9 befindet sich jetzt an der 5. Position von Array B, daher gibt es 4 Inversionen. Wir wissen dies, weil 9 an erster Stelle in Array A stand, sodass jedes nachfolgend erscheinende Element mit niedrigerem Wert einen Index von j> i haben würde (da i in diesem Fall wieder 1 ist). Entfernen Sie A [1] aus Array A und auch aus der entsprechenden Position in Array B (fett gedruckte Elemente werden entfernt).
A = ( 9 , 1, 14, 8, 12, 3, 2) = (1, 14, 8, 12, 3, 2)
B = (1, 2, 3, 8, 9 , 12, 14) = (1, 2, 3, 8, 12, 14)
Wenn Sie in diesem Sinne fortfahren, erhalten Sie die Gesamtzahl der Inversionen für Array A, sobald die Schleife abgeschlossen ist.
Schritt 1 (Zusammenführungssortierung) würde O (n * log n) zur Ausführung benötigen. Schritt 2 würde n-mal ausgeführt und bei jeder Ausführung eine binäre Suche durchführen, bei der O (log n) für insgesamt O (n * log n) ausgeführt wird. Die Gesamtlaufzeit wäre somit O (n * log n) + O (n * log n) = O (n * log n).
Danke für Ihre Hilfe. Das Aufschreiben der Beispielarrays auf ein Blatt Papier half wirklich, das Problem zu visualisieren.
quelle
In Python
quelle
Ich frage mich, warum noch niemand binär indizierte Bäume erwähnt hat. Sie können eine verwenden, um Präfixsummen für die Werte Ihrer Permutationselemente zu verwalten. Dann können Sie einfach von rechts nach links fortfahren und für jedes Element die Anzahl der Elemente zählen, die kleiner als rechts sind:
Die Komplexität ist O (n log n) und der konstante Faktor ist sehr gering.
quelle
i -= i & -i
Linie? Und ähnlichi += i & -i
timeit
Vergleich aller Python-Antworten auf diese Frage enthält, sodass sie Ihren Code enthält. Möglicherweise möchten Sie sich die Timing-Ergebnisse ansehen.Ich hatte eine ähnliche Frage für Hausaufgaben. Ich war eingeschränkt, dass es O (nlogn) Effizienz haben muss.
Ich habe die von Ihnen vorgeschlagene Idee der Verwendung von Mergesort verwendet, da diese bereits die richtige Effizienz aufweist. Ich habe gerade einen Code in die Zusammenführungsfunktion eingefügt, der im Grunde war: Immer wenn eine Zahl aus dem Array rechts zum Ausgabearray hinzugefügt wird, addiere ich zur Gesamtzahl der Inversionen die Anzahl der im linken Array verbleibenden Zahlen.
Das macht für mich jetzt sehr viel Sinn, da ich genug darüber nachgedacht habe. Sie zählen, wie oft eine größere Zahl vor einer Zahl steht.
hth.
quelle
Der Hauptzweck dieser Antwort ist es, die Geschwindigkeiten der verschiedenen Python-Versionen zu vergleichen, aber ich habe auch einige eigene Beiträge. (FWIW, ich habe diese Frage gerade bei einer doppelten Suche entdeckt).
Die relativen Ausführungsgeschwindigkeiten von in CPython implementierten Algorithmen können sich von denen unterscheiden, die man von einer einfachen Analyse der Algorithmen und von Erfahrungen mit anderen Sprachen erwarten würde. Dies liegt daran, dass Python viele leistungsstarke Funktionen und Methoden bietet, die in C implementiert sind und Listen und andere Sammlungen mit der Geschwindigkeit bearbeiten können, die in einer vollständig kompilierten Sprache erreicht wird. Daher werden diese Vorgänge viel schneller ausgeführt als gleichwertige Algorithmen, die "manuell" mit Python implementiert wurden Code.
Code, der diese Tools nutzt, kann häufig theoretisch überlegene Algorithmen übertreffen, die versuchen, alles mit Python-Operationen für einzelne Elemente der Sammlung zu tun. Dies wirkt sich natürlich auch auf die tatsächlich verarbeitete Datenmenge aus. Bei moderaten Datenmengen kann Code, der einen O (n²) -Algorithmus verwendet, der mit C-Geschwindigkeit ausgeführt wird, leicht einen O (n log n) -Algorithmus schlagen, der den Großteil seiner Arbeit mit einzelnen Python-Operationen erledigt.
Viele der veröffentlichten Antworten auf diese Frage zur Inversionszählung verwenden einen auf Mergesort basierenden Algorithmus. Theoretisch ist dies ein guter Ansatz, es sei denn, die Arraygröße ist sehr klein. Der in Python integrierte TimSort (ein hybrider stabiler Sortieralgorithmus, der aus der Sortierung von Zusammenführungen und Einfügungen abgeleitet wird) wird jedoch mit C-Geschwindigkeit ausgeführt, und ein in Python von Hand codierter Mergesort kann nicht hoffen, mit ihm um Geschwindigkeit zu konkurrieren.
Eine der faszinierenderen Lösungen in der Antwort von Niklas B verwendet die integrierte Sortierung, um die Rangfolge der Array-Elemente zu bestimmen, und einen binären indizierten Baum (auch bekannt als Fenwick-Baum), um die zur Berechnung der Inversion erforderlichen kumulativen Summen zu speichern Anzahl. Während ich versuchte, diese Datenstruktur und den Algorithmus von Niklas zu verstehen, schrieb ich einige eigene Variationen (siehe unten). Ich habe aber auch festgestellt, dass es für moderate Listengrößen tatsächlich schneller ist , die integrierte
sum
Funktion von Python zu verwenden als den schönen Fenwick-Baum.Wenn die Listengröße etwa 500 erreicht, zeigt der O (n²) -Aspekt des Aufrufs
sum
in dieserfor
Schleife schließlich seinen hässlichen Kopf und die Leistung beginnt zu sinken.Mergesort ist nicht die einzige O-Sorte (nlogn), und mehrere andere können verwendet werden, um die Inversionszählung durchzuführen. prasadvks Antwort verwendet eine binäre Baumsortierung, sein Code scheint jedoch in C ++ oder einer seiner Ableitungen zu sein. Also habe ich eine Python-Version hinzugefügt. Ich habe ursprünglich eine Klasse verwendet, um die Baumknoten zu implementieren, aber festgestellt, dass ein Dikt spürbar schneller ist. Ich habe schließlich list verwendet, was sogar noch schneller ist, obwohl es den Code etwas weniger lesbar macht.
Ein Bonus von TreeSort ist, dass es viel einfacher ist, iterativ zu implementieren als Mergesort. Python optimiert die Rekursion nicht und hat eine Rekursionstiefenbegrenzung (obwohl diese erhöht werden kann, wenn Sie sie wirklich benötigen). Und natürlich sind Python-Funktionsaufrufe relativ langsam. Wenn Sie also versuchen, die Geschwindigkeit zu optimieren, sollten Sie Funktionsaufrufe vermeiden, wenn dies praktisch ist.
Eine andere O (nlogn) -Sorte ist die ehrwürdige Radix-Sorte. Der große Vorteil ist, dass die Schlüssel nicht miteinander verglichen werden. Es ist Nachteil ist , dass es am besten auf zusammenhängenden Sequenzen von ganzen Zahlen arbeitet, idealerweise eine Permutation der ganzen Zahlen in
range(b**m)
denenb
in der Regel ist 2. Ich habe ein paar Versionen auf radix , zugegeben sortiert nach dem Versuch zu lesen Zählen Inversionen, Offline - Orthogonal Bereich Zählen, und damit verbundenen Problemen , die sind verknüpft bei der Berechnung der Anzahl der "Inversionen" in einer Permutation .Um die Radix-Sortierung effektiv zu verwenden, um Inversionen in einer allgemeinen Folge
seq
der Länge n zu zählen, können wir eine Permutation erstellenrange(n)
, die die gleiche Anzahl von Inversionen wie hatseq
. Wir können das in (im schlimmsten Fall) O (nlogn) Zeit über TimSort tun. Der Trick besteht darin, die Indizesseq
durch Sortieren zu permutierenseq
. Es ist einfacher, dies mit einem kleinen Beispiel zu erklären.Ausgabe
Durch Sortieren der (Wert-, Index-) Paare von haben
seq
wir die Indizesseq
mit der gleichen Anzahl von Swaps permutiert , die erforderlich sind,seq
um aus der sortierten Reihenfolge in die ursprüngliche Reihenfolge zu bringen. Wir können diese Permutation erstellen, indem wirrange(n)
mit einer geeigneten Schlüsselfunktion sortieren :Ausgabe
Wir können das vermeiden ,
lambda
indem Sieseq
‚s -.__getitem__
Methode:Dies ist nur geringfügig schneller, aber wir suchen nach allen Geschwindigkeitsverbesserungen, die wir bekommen können. ;)
Der folgende Code führt
timeit
Tests mit allen auf dieser Seite vorhandenen Python-Algorithmen sowie einige meiner eigenen durch: einige Brute-Force-O (n²) -Versionen, einige Variationen des Niklas B-Algorithmus und natürlich eine auf Mergesort basierende (was ich geschrieben habe, ohne auf die vorhandenen Antworten Bezug zu nehmen). Es enthält auch meinen listenbasierten Baumsort-Code, der grob vom prasadvk-Code abgeleitet ist, und verschiedene Funktionen, die auf der Radix-Sortierung basieren, wobei einige eine ähnliche Strategie wie die Mergesort-Ansätze verwenden und anderesum
einen Fenwick-Baum verwenden.Dieses Programm misst die Ausführungszeit jeder Funktion anhand einer Reihe zufälliger Listen von Ganzzahlen. Es kann auch überprüft werden, ob jede Funktion dieselben Ergebnisse wie die anderen liefert und die Eingabeliste nicht ändert.
Jeder
timeit
Aufruf ergibt einen Vektor mit 3 Ergebnissen, die ich sortiere. Der Hauptwert, der hier betrachtet werden muss, ist der Mindestwert. Die anderen Werte geben lediglich einen Hinweis darauf, wie zuverlässig dieser Mindestwert ist, wie im Hinweis in dentimeit
Moduldokumenten erläutert .Leider ist die Ausgabe dieses Programms zu groß, um in diese Antwort aufgenommen zu werden, daher veröffentliche ich sie in einer eigenen Antwort (Community-Wiki) .
Die Ausgabe erfolgt von 3 Läufen auf meinem alten 32-Bit-Single-Core-2-GHz-Computer, auf dem Python 3.6.0 auf einer alten Debian-Derivat-Distribution ausgeführt wird. YMMV. Während der Tests habe ich meinen Webbrowser heruntergefahren und die Verbindung zu meinem Router getrennt, um die Auswirkungen anderer Aufgaben auf die CPU zu minimieren.
Der erste Lauf testet alle Funktionen mit Listengrößen von 5 bis 320 und Schleifengrößen von 4096 bis 64 (da sich die Listengröße verdoppelt, halbiert sich die Schleifengröße). Der zufällige Pool, der zum Erstellen jeder Liste verwendet wird, ist halb so groß wie die Liste selbst, sodass wir wahrscheinlich viele Duplikate erhalten. Einige der Inversionszählalgorithmen reagieren empfindlicher auf Duplikate als andere.
Der zweite Durchlauf verwendet größere Listen: 640 bis 10240 und eine feste Schleifengröße von 8. Um Zeit zu sparen, werden einige der langsamsten Funktionen aus den Tests entfernt. Meine Brute-Force-O (n²) -Funktionen sind bei diesen Größen einfach viel zu langsam, und wie bereits erwähnt, kann mein verwendeter Code
sum
, der auf kleinen bis mittleren Listen so gut funktioniert, auf großen Listen einfach nicht mithalten.Der letzte Durchlauf umfasst Listengrößen von 20480 bis 655360 und eine feste Schleifengröße von 4 mit den 8 schnellsten Funktionen. Bei Listengrößen unter etwa 40.000 ist der Code von Tim Babych der klare Gewinner. Gut gemacht, Tim! Der Code von Niklas B ist auch ein guter Allrounder, obwohl er auf den kleineren Listen geschlagen wird. Der halbierungsbasierte Code von "Python" funktioniert auch ziemlich gut, obwohl er mit riesigen Listen mit vielen Duplikaten etwas langsamer zu sein scheint, wahrscheinlich aufgrund der linearen
while
Schleife, die zum Überspringen von Dupes verwendet wird.Bei sehr großen Listen können die halbierungsbasierten Algorithmen jedoch nicht mit den echten O (nlogn) -Algorithmen konkurrieren.
Die Ausgabe finden Sie hier
quelle
bisect
ist C? Ich bin mir ziemlich sicher, dass es Python ist.Die Anzahl der Inversionen kann durch Analysieren des Zusammenführungsprozesses in Zusammenführungssortierung ermittelt werden:
Wenn Sie ein Element aus dem zweiten Array in das Zusammenführungsarray (die 9 in diesem Beispiel) kopieren, behält es seinen Platz relativ zu anderen Elementen. Beim Kopieren eines Elements vom ersten Array in das Zusammenführungsarray (hier die 5) wird es invertiert, wobei alle Elemente im zweiten Array verbleiben (2 Inversionen mit der 3 und der 4). Eine kleine Modifikation der Zusammenführungssortierung kann also das Problem in O (n ln n) lösen.
Zum Beispiel kommentieren Sie einfach die beiden # Zeilen im Mergesort-Python-Code unten aus, um die Anzahl zu erhalten.
BEARBEITEN 1
Die gleiche Aufgabe kann mit einer stabilen Version der schnellen Sortierung erreicht werden, die bekanntermaßen etwas schneller ist:
Wenn Sie Pivot als letztes Element auswählen, werden Inversionen gut gezählt und die Ausführungszeit ist 40% besser als bei der Zusammenführung.
BEARBEITEN 2
Für die Leistung in Python eine Numpy & Numba-Version:
Zuerst der numpy Teil, der argsort O (n ln n) verwendet:
Und der numba-Teil für den effizienten BIT-Ansatz :
quelle
timeit
Vergleich aller Python-Antworten auf diese Frage enthält, sodass sie Ihren Code enthält. Möglicherweise möchten Sie sich die Timing-Ergebnisse ansehen.timeit
Sammlung aufzunehmen.Beachten Sie, dass die Antwort von Geoffrey Irving falsch ist.
Nehmen Sie als Beispiel die Sequenz {3, 2, 1}. Es gibt drei Inversionen: (3, 2), (3, 1), (2, 1), daher ist die Inversionszahl 3. Nach der angegebenen Methode wäre die Antwort jedoch 2 gewesen.
quelle
Überprüfen Sie dies: http://www.cs.jhu.edu/~xfliu/600.363_F03/hw_solution/solution1.pdf
Ich hoffe, dass es Ihnen die richtige Antwort gibt.
quelle
Hier ist eine mögliche Lösung mit Variation des Binärbaums. Es fügt jedem Baumknoten ein Feld mit dem Namen rightSubTreeSize hinzu. Fügen Sie die Zahl weiterhin in der Reihenfolge in den Binärbaum ein, in der sie im Array angezeigt wird. Wenn die Anzahl lhs des Knotens beträgt, beträgt die Inversionszahl für dieses Element (1 + rightSubTreeSize). Da alle diese Elemente größer als das aktuelle Element sind und früher im Array erschienen wären. Wenn das Element zu rhs eines Knotens wechselt, erhöhen Sie einfach dessen rightSubTreeSize. Es folgt der Code.
quelle
if(p->data < q->data)
da Duplikate sonst nicht korrekt behandelt werden. Und es besteht keine Notwendigkeit,q
am oberen Rand der Schleife zu testen. Eine bedingungslosewhile
Schleife funktioniert einwandfrei. Außerdem haben Sie es versäumt zu erwähnen, um welche Sprache es sich handelt. :) Und Ihre Funktion scheint ihre Kopfzeile verloren zu haben.quelle
Da dies eine alte Frage ist, werde ich meine Antwort in C geben.
quelle
Hier ist eine C ++ - Lösung
quelle
Hier ist ein C-Code für Zählumkehrungen
Eine ausführliche Erklärung wurde hier gegeben: http://www.geeksforgeeks.org/counting-inversions/
quelle
O (n log n) Zeit, O (n) Raumlösung in Java.
Ein Mergesort mit einer Optimierung, um die Anzahl der während des Merge-Schritts durchgeführten Inversionen beizubehalten. (Eine gut erläuterte Zusammenführung finden Sie unter http://www.vogella.com/tutorials/JavaAlgorithmsMergesort/article.html. )
Da Mergesort an Ort und Stelle durchgeführt werden kann, kann die Raumkomplexität auf O (1) verbessert werden.
Bei Verwendung dieser Sortierung erfolgen die Inversionen nur im Zusammenführungsschritt und nur dann, wenn ein Element des zweiten Teils vor Elementen aus der ersten Hälfte stehen muss, z
fusioniert mit
Wir haben 3 + 2 + 0 = 5 Inversionen:
Nachdem wir die 5 Inversionen durchgeführt haben, lautet unsere neue zusammengeführte Liste 0, 1, 5, 6, 10, 15, 22
In Codility gibt es eine Demo-Aufgabe namens ArrayInversionCount, mit der Sie Ihre Lösung testen können.
quelle
Hier ist die Perl-Implementierung von O (n * log (n)):
quelle
Meine Antwort in Python:
1- Sortieren Sie zuerst das Array und erstellen Sie eine Kopie davon. In meinem Programm steht B für das sortierte Array. 2- Durchlaufen Sie das ursprüngliche Array (unsortiert) und suchen Sie den Index dieses Elements in der sortierten Liste. Notieren Sie auch den Index des Elements. 3- Stellen Sie sicher, dass das Element keine Duplikate enthält. Wenn dies der Fall ist, müssen Sie den Wert Ihres Index um -1 ändern. Die while-Bedingung in meinem Programm macht genau das. 4- Zählen Sie weiter die Inversion, die Ihren Indexwert ergibt, und entfernen Sie das Element, sobald Sie seine Inversion berechnet haben.
quelle
timeit
Vergleich aller Python-Antworten auf diese Frage enthält, sodass sie Ihren Code enthält. Möglicherweise möchten Sie sich die Timing-Ergebnisse ansehen.Nun, ich habe eine andere Lösung, aber ich befürchte, dass dies nur für bestimmte Array-Elemente funktionieren würde.
Um meinen Code zu erklären, fügen wir weiterhin Elemente vom Ende des Arrays hinzu. Für jedes eingehende Array-Element finden wir den Index des ersten Elements in Vektor v, der größer als unser eingehendes Element ist, und weisen diesen Wert der Inversionszahl des Index des eingehenden Elements zu Danach fügen wir dieses Element an der richtigen Position in den Vektor v ein, sodass der Vektor v in sortierter Reihenfolge bleibt.
quelle
Eine andere Python-Lösung, kurz. Verwendet das integrierte Halbierungsmodul, das Funktionen zum Einfügen eines Elements an seiner Stelle im sortierten Array und zum Suchen des Elementindex im sortierten Array bereitstellt.
Die Idee ist, Elemente, die links von n-ten liegen, in einem solchen Array zu speichern, wodurch wir leicht die Anzahl von Elementen finden können, die größer als n-te sind.
quelle
timeit
Vergleich aller Python-Antworten auf diese Frage enthält, sodass sie Ihren Code enthält. Möglicherweise möchten Sie sich die Timing-Ergebnisse ansehen. : DDiese Antwort enthält die Ergebnisse der
timeit
Tests, die durch den Code in meiner Hauptantwort erzeugt wurden . Bitte sehen Sie diese Antwort für Details!quelle
Die einfache Antwort O (n ^ 2) besteht darin, verschachtelte for-Schleifen zu verwenden und für jede Inversion einen Zähler zu erhöhen
Nun, ich nehme an, Sie wollen eine effizientere Lösung, ich werde darüber nachdenken.
quelle
Eine mögliche Lösung in C ++, die die O (N * log (N)) Zeitkomplexitätsanforderung erfüllt, wäre wie folgt.
Es unterscheidet sich von einer regulären Zusammenführungssortierung nur durch den Zähler.
quelle
Hier ist meine O (n log n) -Lösung in Ruby:
Und einige Testfälle:
quelle
Der beste optimierte Weg besteht darin, das Problem durch Zusammenführen zu lösen. Durch Zusammenführen können wir überprüfen, wie viele Inversionen erforderlich sind, indem wir das linke und das rechte Array vergleichen. Immer wenn das Element im linken Array größer ist als das Element im rechten Array, wird es invertiert.
Sortieransatz zusammenführen: -
Hier ist der Code. Code ist genau das gleiche wie Zusammenführungssortierung, außer Code-Snippet unter
mergeToParent
Methode, bei der ich die Inversion unter sonst Bedingung von zähle(left[leftunPicked] < right[rightunPicked])
Ein weiterer Ansatz, bei dem wir das Eingabearray mit dem sortierten Array vergleichen können: - Diese Implementierung der Diablo-Antwort. Dies sollte jedoch nicht bevorzugt werden, da das Entfernen der n Elemente aus einem Array oder einer Liste log (n ^ 2) ist.
quelle
Die maximale Anzahl von Inversionen, die für eine Liste mit Größen
n
möglich sind, kann durch einen Ausdruck verallgemeinert werden:Für ein Array mit einer Größe sind also
6
maximal mögliche Inversionen gleich15
.Um eine Komplexität von zu erreichen,
n logn
könnten wir den Inversionsalgorithmus beim Zusammenführen sortieren.Hier sind die verallgemeinerten Schritte:
inversionCount += leftSubArray.length
Das ist es!
Dies ist ein einfaches Beispiel, das ich mit Javascript erstellt habe:
quelle
Implementierung des Zählens von Inversionen in einem Array mit Zusammenführungssortierung in Swift:
Beachten Sie, dass die Anzahl der Swaps um erhöht wird
(Dies ist die relative Länge der linken Seite des Arrays abzüglich des Index des aktuellen Elements auf der linken Seite.)
... weil dies die Anzahl der Elemente ist, die das Element auf der rechten Seite des Arrays überspringen musste (Anzahl der Inversionen), um sortiert zu werden.
quelle
Die meisten Antworten basieren auf,
MergeSort
aber es ist nicht die einzige Möglichkeit, dies zu lösenO(nlogn)
Ich werde ein paar Ansätze diskutieren.
Benutze einen
Balanced Binary Search Tree
Etwas wie das.
Binary Indexed Tree
Segment Tree
[0, a[i]-1]
und Aktualisierunga[i] with 1
Auch bei Verwendung
BIT
oder istSegment-Tree
eine gute Idee zu tunCoordinate compression
quelle
C ++ Θ (n lg n) Lösung mit dem Drucken von Paaren, die eine Inversionszahl darstellen.
quelle
Verwenden Sie mergesort im Inkremeant-Zähler für den Merge-Schritt, wenn die in die Ausgabe kopierte Nummer vom richtigen Array stammt.
quelle
Ich musste dies kürzlich in R tun:
quelle