WIP: snapshot before THB-wide structural reorganization

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-04 22:19:03 +02:00
parent 3f85063348
commit 7624547d66
10 changed files with 714 additions and 8 deletions

View File

@@ -7,5 +7,14 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/26.0.2/annotations-26.0.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component> </component>
</module> </module>

View File

@@ -55,11 +55,11 @@ public class Sorter {
} }
public static final void main(String[] args) { public static final void main(String[] args) {
int[] array = new int[100]; int[] array = new int[100000000];
Util util = new Util(); Util util = new Util();
util.fillArrayRandom(array, 100); util.fillArrayRandom(array, 10000000);
Sorter mySorter = new Sorter(); Sorter mySorter = new Sorter();
mySorter.sort(array); mySorter.sort(array);
util.printArray(array);
} }
} }

View File

@@ -1,5 +1,6 @@
package task6; package task6;
import org.jetbrains.annotations.NotNull;
import util.Util; import util.Util;
public class SorterAI { public class SorterAI {
@@ -22,7 +23,7 @@ public class SorterAI {
introsort(array, pivot + 1, high, depthLimit - 1); introsort(array, pivot + 1, high, depthLimit - 1);
} }
private int partition(int[] array, int low, int high) { private int partition(int @NotNull [] array, int low, int high) {
int mid = low + (high - low) / 2; int mid = low + (high - low) / 2;
if (array[mid] < array[low]) swap(array, low, mid); if (array[mid] < array[low]) swap(array, low, mid);
if (array[high] < array[low]) swap(array, low, high); if (array[high] < array[low]) swap(array, low, high);
@@ -73,18 +74,17 @@ public class SorterAI {
} }
} }
private void swap(int[] array, int i, int j) { private void swap(int @NotNull [] array, int i, int j) {
int temp = array[i]; int temp = array[i];
array[i] = array[j]; array[i] = array[j];
array[j] = temp; array[j] = temp;
} }
public static final void main(String[] args) { public static final void main(String[] args) {
int[] array = new int[100]; int[] array = new int[1000000000];
Util util = new Util(); Util util = new Util();
util.fillArrayRandom(array, 100); util.fillArrayRandom(array, 100000000);
SorterAI mySorter = new SorterAI(); SorterAI mySorter = new SorterAI();
mySorter.sort(array); mySorter.sort(array);
util.printArray(array);
} }
} }

Binary file not shown.

344
aufgabenblatt2/solution.md Normal file
View File

@@ -0,0 +1,344 @@
# Aufgabenblatt 2 Lösungen
## Aufgabe 1: Maximalwerte in sortierten Arrays
### Eigene Lösung
Implementierung in `src/util/Util.java`:
```java
public int findMaxBySorting(int[] array){
Sorter sorter = new Sorter();
sorter.sort(array);
return array[array.length - 1];
}
```
**Idee:** Nach dem aufsteigenden Sortieren steht der größte Wert zwangsläufig am Ende des Arrays. Es genügt daher ein einziger Zugriff auf `array[array.length - 1]`, um den Maximalwert zurückzugeben.
### Laufzeitkomplexität (ohne Sortieren)
**O(1)** konstante Laufzeit.
Nach dem Sortieren wird nur eine einzige Operation durchgeführt: der Zugriff auf das letzte Array-Element. Diese Operation ist unabhängig von der Array-Größe `n` und benötigt immer dieselbe Zeit. Es gibt keine Schleife, keine Rekursion und keinen Vergleich lediglich einen Index-Zugriff.
(Die Gesamtkomplexität *inklusive* Sortieren liegt bei O(n²) bei Insertion-Sort bzw. O(n log n) bei Merge-Sort.)
### Vergleich mit KI-Lösung
Der KI (Claude) wurde die Aufgabenstellung inklusive der Methodensignatur `public int findMaxBySorting(int[] array)` und der Information übergeben, dass eine `insertionSort()`-Methode zur Verfügung steht. Die KI lieferte folgende Lösung:
```java
public int findMaxBySorting(int[] array) {
if (array == null || array.length == 0) {
throw new IllegalArgumentException("Array darf nicht leer sein");
}
insertionSort(array);
return array[array.length - 1];
}
```
**Gemeinsamkeiten:**
- Gleiche Grundidee: erst sortieren, dann das letzte Element zurückgeben.
- Gleiche Laufzeitkomplexität des Nach-dem-Sortieren-Teils: O(1).
- Identische Methodensignatur.
**Unterschiede:**
- Die KI ruft `insertionSort()` direkt als Methode derselben Klasse auf. Meine Lösung verwendet die ausgelagerte `Sorter`-Klasse und erzeugt dafür eine Instanz. Das entspricht stärker dem OOP-Gedanken, dass Sortieren eine eigene Verantwortung ist.
- Die KI fügt eine explizite Null-/Leer-Prüfung mit `IllegalArgumentException` hinzu. Meine Lösung verzichtet darauf und würde bei einem leeren Array eine `ArrayIndexOutOfBoundsException` werfen. Die KI-Variante ist robuster, meine Variante kompakter.
- Die KI nutzt die im Aufgabentext vorgesehene `insertionSort()`-Methode. In meiner aktuellen `Sorter`-Klasse ist `sort()` ein Natural-Merge-Sort aus Aufgabenblatt 1. Funktional liefern beide das korrekte sortierte Ergebnis.
### KI-Einschätzung zur Laufzeitkomplexität
**KI-Antwort (wörtlich):**
> "Lässt man den Aufwand für das Sortieren außer Acht, hat die Methode eine Laufzeitkomplexität von **O(1)**, da nach dem Sortieren lediglich ein einzelner Array-Zugriff auf das letzte Element (`array[array.length - 1]`) durchgeführt wird. Dieser Zugriff ist unabhängig von der Array-Größe in konstanter Zeit möglich. Die Gesamtkomplexität der Methode inklusive Sortierung wird hingegen vom Insertion-Sort dominiert und liegt im Worst-Case bei O(n²)."
**Eigene Bewertung der Abschätzung:**
Die Einschätzung der KI ist **korrekt**. Der Zugriff auf ein Element über einen bekannten Index ist in Java eine Operation mit konstantem Aufwand, daher ist O(1) ohne den Sortieranteil richtig begründet. Auch die ergänzende Aussage zur Gesamtkomplexität stimmt: Insertion-Sort hat im Worst-Case quadratischen Aufwand, der damit das O(1) des Maximalwert-Zugriffs vollständig dominiert. Die Antwort ist präzise, vollständig und enthält keine Fehleinschätzung.
## Aufgabe 2: Werte in sortierten Arrays finden
### Eigene Lösung
Implementierung in `src/util/Util.java`:
```java
public Integer findValueBySorting(int @NotNull [] array, int value){
Sorter sorter = new Sorter();
sorter.sort(array);
int min = array[0];
int max = array[array.length - 1];
if (value < min || value > max){
return null;
}
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
if (array[mid] == value) {
return mid;
} else if (array[mid] < value) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return null;
}
```
**Idee:** Da das Array nach dem Sortieren aufsteigend geordnet ist, kann genau die gleiche Strategie wie beim Zahlenraten aus Aufgabenblatt 1 (Methode `autoGuess()`) verwendet werden: **Binäre Suche**. In jedem Schritt wird die mittlere Position des aktuell betrachteten Bereichs geprüft. Ist der mittlere Wert gleich dem gesuchten Wert, ist die Position gefunden. Ist er zu klein, wird nur die rechte Hälfte weiter durchsucht; ist er zu groß, nur die linke. Dadurch halbiert sich der Suchbereich in jedem Schritt.
Zusätzlich wird zu Beginn geprüft, ob der gesuchte Wert überhaupt im Wertebereich des Arrays (`min`..`max`) liegt. Falls nicht, kann direkt `null` zurückgegeben werden, ohne die Suche zu starten.
Der Ausdruck `(low + high) >>> 1` berechnet die Mitte ohne die Möglichkeit eines Integer-Overflows bei sehr großen Arrays.
### Laufzeitkomplexität (ohne Sortieren)
**O(log n)** logarithmische Laufzeit.
Begründung: In jedem Schleifendurchlauf wird der verbleibende Suchbereich halbiert. Nach `k` Durchläufen ist der Bereich von ursprünglich `n` Elementen auf `n / 2^k` reduziert. Die Suche endet spätestens, wenn der Bereich leer ist, also bei `k = log₂(n)`. Die Laufzeit wächst damit logarithmisch mit der Array-Größe.
Die vorgelagerte Bereichsprüfung (`value < min || value > max`) ist O(1) und ändert die Gesamtkomplexität der Suche nicht.
### Vergleich mit KI-Lösung
Der KI wurde die Aufgabenstellung sowie der Hinweis auf das Zahlenraten (`autoGuess()`) aus dem vorherigen Aufgabenblatt übergeben. Die KI-Lösung (Claude):
```java
public Integer findValueBySorting(int[] array, int value) {
if (array == null || array.length == 0) return null;
insertionSort(array);
int left = 0;
int right = array.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (array[mid] == value) {
return mid;
} else if (array[mid] < value) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return null;
}
```
**Gemeinsamkeiten:**
- Identische Grundidee: Sortieren + binäre Suche (das "Zahlenraten"-Prinzip aus `autoGuess()`).
- Gleiche Schleifenstruktur mit `left`/`right` bzw. `low`/`high` und Halbierung über die Mitte.
- Gleiche Rückgabesemantik: Index bei Treffer, sonst `null`.
- Identische Laufzeitkomplexität O(log n) für den Such-Teil.
**Unterschiede:**
- Die KI nutzt `left + (right - left) / 2` zur Berechnung der Mitte, meine Lösung `(low + high) >>> 1`. Beide Varianten sind overflow-sicher (die KI-Variante über Subtraktion, meine über die vorzeichenlose Rechtsverschiebung). Die klassische naive Form `(low + high) / 2` würde beide nicht verwenden.
- Die KI prüft `array == null || array.length == 0` als Einstieg und gibt frühzeitig `null` zurück. Meine Lösung nutzt stattdessen `@NotNull` als Annotation und prüft den Wertebereich über `min`/`max`. Das spart bei Werten außerhalb des Bereichs alle Iterationen der Binärsuche.
- Die KI ruft `insertionSort()` direkt intern auf; meine Lösung verwendet die externe `Sorter`-Klasse.
- Die KI führt keine Min-/Max-Prüfung durch meine Lösung erkennt dadurch `null`-Fälle ohne Iteration in O(1), während die KI-Variante immer eine volle Binärsuche durchführt (was immer noch O(log n) ist, aber unnötige Vergleiche macht).
## Aufgabe 3: Merge-Sort
### Eigene Lösung
Für Aufgabenblatt 1 hatte ich in der `Sorter`-Klasse bereits eine Variante des Merge-Sorts implementiert (Methode `sort()`). Diese arbeitet als **Natural-Merge-Sort**: iterativ/bottom-up, findet natürliche aufsteigende Runs und verschmilzt sie paarweise, bis nur noch ein Run übrig ist.
Zusätzlich habe ich für diese Aufgabe eine klassische **rekursive Top-Down-Variante** als `mergeSort(int[] array)` ergänzt (in `src/util/Sorter.java`). Das ist die Variante, die als Grundlage für Aufgabe 4 (`optimizedMergeSort`) dient, da die Insertion-Sort-Schwelle bei ≤ 10 Elementen nur bei rekursiver Unterteilung sinnvoll an jeder Teilungsebene greift.
```java
public void mergeSort(int[] array) {
if (array == null || array.length <= 1) return;
int[] temp = new int[array.length];
mergeSortRecursive(array, temp, 0, array.length - 1);
}
private void mergeSortRecursive(int[] array, int[] temp, int left, int right) {
if (left >= right) return;
int mid = (left + right) >>> 1;
mergeSortRecursive(array, temp, left, mid);
mergeSortRecursive(array, temp, mid + 1, right);
merge(array, temp, left, mid, right);
}
```
Die bereits existierende private `merge(…)`-Methode wird wiederverwendet. Ein einzelner `temp`-Buffer in der Größe des Arrays wird einmal allokiert und durch alle Rekursionsebenen durchgereicht das vermeidet wiederholte Allokationen.
### Vergleich mit KI-Lösung
Der KI wurde die Aufgabenstellung übergeben. Die KI lieferte eine sehr ähnliche rekursive Top-Down-Variante:
```java
public void mergeSort(int[] array) {
if (array.length < 2) return;
int mid = array.length / 2;
int[] left = new int[mid];
int[] right = new int[array.length - mid];
System.arraycopy(array, 0, left, 0, mid);
System.arraycopy(array, mid, right, 0, array.length - mid);
mergeSort(left);
mergeSort(right);
merge(array, left, right);
}
private void merge(int[] array, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
while (i < left.length && j < right.length) {
array[k++] = (left[i] <= right[j]) ? left[i++] : right[j++];
}
while (i < left.length) array[k++] = left[i++];
while (j < right.length) array[k++] = right[j++];
}
```
**Gemeinsamkeiten:**
- Gleicher Grundalgorithmus: rekursive Halbierung bis zur Basisgröße 1, dann paarweises Mischen.
- Gleiche Laufzeitkomplexität O(n log n) im Average- und Worst-Case.
- Beide sind stabil.
**Unterschiede:**
- **Speicherverwaltung:** Die KI-Variante allokiert in jedem Rekursionsschritt zwei neue Subarrays (`left` und `right`) und kopiert via `System.arraycopy` hinein. Meine Variante allokiert genau einen `temp`-Buffer der Gesamtgröße und arbeitet mit Indizes darauf. Meine Variante ist speichereffizienter (Gesamt-Zusatzspeicher O(n) statt O(n log n) durch die Rekursions-Kopien).
- **Signatur des Mischens:** Die KI übergibt Subarrays, meine Lösung arbeitet mit Index-Grenzen `(left, mid, right)` auf einem gemeinsamen Puffer. Meine Form ist in der Lehrbuchtradition (z.B. Cormen CLRS) üblich.
- **Index-Mittelpunkt:** Meine Lösung nutzt `(left + right) >>> 1` (overflow-sicher); die KI nutzt `array.length / 2` im jeweiligen Rekursionsframe, was durch die neuen Subarrays das Overflow-Problem umgeht.
- **Besonderheit meiner AB1-Lösung:** Zusätzlich existiert `sort()` als Natural-Merge-Sort iterative Variante, die bereits sortierte Teilsequenzen im Input ausnutzt und für fast-sortierte Arrays deutlich schneller als die klassische Variante ist. Die KI hat diese Variante nicht angeboten; sie ist eine Optimierung, die man explizit anfragen müsste.
Insgesamt ist die Varianz bei Merge-Sort wie erwartet hoch. Die Algorithmenidee ist identisch, die Implementierungsdetails (Speicher, Indizes vs. Subarrays) unterscheiden sich deutlich.
## Aufgabe 4: Optimierter Merge-Sort
### Eigene Lösung
Zunächst wurde `insertionSort(int[] array)` in `src/util/Sorter.java` implementiert (klassischer Insertion-Sort):
```java
public void insertionSort(int[] array) {
if (array == null || array.length <= 1) return;
for (int i = 1; i < array.length; i++) {
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
```
Der Inhalt von `mergeSort` wurde dann in `optimizedMergeSort` kopiert und so angepasst, dass für Teilbereiche der Größe ≤ 10 statt weiterer Rekursion direkt Insertion-Sort auf dem Bereich läuft:
```java
public void optimizedMergeSort(int[] array) {
if (array == null || array.length <= 1) return;
int[] temp = new int[array.length];
optimizedMergeSortRecursive(array, temp, 0, array.length - 1);
}
private void optimizedMergeSortRecursive(int[] array, int[] temp, int left, int right) {
if (left >= right) return;
if (right - left + 1 <= 10) {
insertionSortRange(array, left, right);
return;
}
int mid = (left + right) >>> 1;
optimizedMergeSortRecursive(array, temp, left, mid);
optimizedMergeSortRecursive(array, temp, mid + 1, right);
merge(array, temp, left, mid, right);
}
private void insertionSortRange(int[] array, int left, int right) {
for (int i = left + 1; i <= right; i++) {
int key = array[i];
int j = i - 1;
while (j >= left && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
```
Wichtig: Der In-Place-Helper `insertionSortRange(array, left, right)` arbeitet direkt auf dem Bereich des Haupt-Arrays, damit die Sortierung konsistent mit dem anschließenden `merge(…)` über die Indizes bleibt. Eine Kopie in ein neues Subarray wäre für kleine Bereiche zwar möglich, aber überflüssig.
Getestet mit `testOptimizedMergeSort()` (ebenfalls in `Sorter.java`).
### KI-Antwort: Wie heißt dieses Sortierverfahren? Tipps?
> "Das Verfahren, das du hier implementiert hast, ist eine klassische **Hybrid-Sortierung** bzw. **getunter Merge-Sort** (engl. *tuned merge sort*). Es nutzt die Tatsache, dass Insertion-Sort für kleine Arrays aufgrund sehr niedriger konstanter Faktoren und Cache-Freundlichkeit praktisch schneller ist als Merge-Sort, obwohl es asymptotisch bei O(n²) liegt. Das Prinzip ist genau dasselbe, das in der Praxis z.B. in **Timsort** (Pythons Standard-Sortierung, Javas `Arrays.sort` für Objekte) oder in Sedgewicks *practical mergesort* verwendet wird.
>
> Ein paar Tipps:
> 1. **Schwellwert empirisch wählen:** Je nach Hardware, JIT und Datentyp liegt das Optimum zwischen ca. 7 und 32. 10 ist ein guter Standardwert, aber Benchmarks lohnen sich. Sedgewick empfiehlt z.B. 15.
> 2. **Small-run detection:** In Timsort werden zusätzlich natürliche aufsteigende/absteigende Läufe erkannt und nur kurze Läufe per Insertion-Sort verlängert. Das ist eine natürliche Weiterentwicklung deiner Optimierung.
> 3. **Merge vermeiden, wenn schon sortiert:** Eine billige Prüfung `array[mid] <= array[mid+1]` vor dem Mischen kann in fast-sortierten Eingaben viele Merge-Aufrufe einsparen.
> 4. **Iterativ statt rekursiv:** Für sehr große Arrays lohnt sich eine Bottom-Up-Variante (wie dein `sort()`), da sie weniger Methodenaufruf-Overhead hat und besser vorhersagbare Cache-Zugriffe erzeugt.
> 5. **Alloc-freies Merging:** Der gemeinsame `temp`-Puffer ist bereits gut gelöst häufiger Fehler in naiven Varianten ist pro Merge-Schritt neu zu allokieren."
Die KI-Antwort ist inhaltlich korrekt und enthält keine Fehleinschätzung. Die Bezeichnung als "getuner Merge-Sort" bzw. "Hybrid-Merge-Sort" mit Verweis auf Timsort trifft zu. Die Tipps sind realistisch und entsprechen dem, was man in Lehrbüchern (Sedgewick, CLRS) und in produktiven Sortier-Implementierungen tatsächlich sieht. Besonders der Hinweis auf die `array[mid] <= array[mid+1]`-Vorprüfung ist eine etablierte, einfach umzusetzende Zusatzoptimierung.
## Aufgabe 5: Umgekehrte Sortierreihenfolge
Die drei Methoden `insertionSort`, `mergeSort` und `optimizedMergeSort` wurden so angepasst, dass sie absteigend sortieren (größter Wert vorne, kleinster Wert hinten).
### Vorüberlegung
Das zugrunde liegende Prinzip ist einfach: In einem Vergleichs-Sortierer legt genau **ein einziger Vergleichsoperator** die Ordnung fest. Dreht man ihn um, kippt die gesamte Ordnung. Konkret werden zwei Arten von Vergleichen umgedreht:
| Verfahren | Relevanter Vergleich (aufsteigend) | Umgedreht (absteigend) |
|--------------|-------------------------------------|------------------------|
| Insertion | `array[j] > key` (größere schieben rechts) | `array[j] < key` (kleinere schieben rechts) |
| Merge | `temp[i] <= temp[j]` (links wählen bei ≤) | `temp[i] >= temp[j]` (links wählen bei ≥) |
### Wichtige Randbedingung: `sort()` und `merge()` bleiben unverändert
Die alte `sort()`-Methode (Natural-Merge-Sort aus Aufgabenblatt 1) und die private `merge()`-Methode bleiben unverändert aufsteigend, weil sie von `Util.findMaxBySorting` und `Util.findValueBySorting` (Aufgaben 1 und 2) benötigt werden. Dort würde eine absteigende Sortierung z.B. dazu führen, dass `array[array.length - 1]` nicht mehr den Maximalwert liefert, und die binäre Suche würde ebenfalls nicht mehr funktionieren. Eine einfache Änderung direkt an `merge()` hätte diese Methoden deshalb kaputt gemacht.
Lösung: es wurde eine neue private Methode `mergeDescending(…)` als Spiegelbild von `merge(…)` hinzugefügt, bei der der Merge-Vergleich umgedreht ist.
### Konkrete Änderungen in `src/util/Sorter.java`
**1. `insertionSort(int[] array)`** Schleifenbedingung umgedreht:
```diff
- while (j >= 0 && array[j] > key) {
+ while (j >= 0 && array[j] < key) {
```
Folge: Es werden jetzt nur noch Elemente nach rechts verschoben, die *kleiner* als `key` sind. Größere Elemente bleiben links größte Werte wandern an den Anfang.
**2. `mergeSortRecursive(…)`** Aufruf der Merge-Methode umgestellt:
```diff
- merge(array, temp, left, mid, right);
+ mergeDescending(array, temp, left, mid, right);
```
**3. `optimizedMergeSortRecursive(…)`** gleicher Aufruf-Wechsel:
```diff
- merge(array, temp, left, mid, right);
+ mergeDescending(array, temp, left, mid, right);
```
**4. `insertionSortRange(…)`** (Helper für `optimizedMergeSort`) Schleifenbedingung umgedreht:
```diff
- while (j >= left && array[j] > key) {
+ while (j >= left && array[j] < key) {
```
**5. Neue private Methode `mergeDescending(…)`** identisch zu `merge(…)`, nur der Merge-Vergleich ist umgedreht:
```diff
- if (temp[i] <= temp[j]) {
+ if (temp[i] >= temp[j]) {
```
### Zusammenfassung
Drei Methoden sortieren jetzt absteigend. Die semantische Änderung besteht jeweils aus einem einzigen umgedrehten Vergleichsoperator. Weil die neue private `mergeDescending`-Methode eingeführt wurde, konnten Aufgabe 1 und Aufgabe 2 ihre ursprüngliche aufsteigende `sort()`-Grundlage behalten und funktionieren unverändert.

View File

@@ -0,0 +1,14 @@
package util;
public class ArrayTester {
public static final void main(String[] args)
{
Util util = new Util();
int num = 60;
int[] array0 = new int[num];
int[] array1 = new int[num];
util.fillArrayRandom(array0, num*100);
util.fillArrayRandom(array1, num*100);
System.out.println(util.firstMatch(array0, array1));
}
}

View File

@@ -0,0 +1,20 @@
package util;
public class IntArrayList {
int lenght = 4;
int lastUnfilledPos = 0;
int[] arr = new int[4];
public IntArrayList(){}
public void add(int a){
if (lastUnfilledPos < lenght){
arr[lastUnfilledPos] = a;
lastUnfilledPos ++;
}
else {
int[] arrTemp = new int[lenght*2];
}
}
}

View File

@@ -0,0 +1,156 @@
package util;
public class Sorter {
public void sort(int[] array) {
if (array == null || array.length <= 1) return;
int n = array.length;
int[] temp = new int[n];
while (true) {
int i = 0;
int merges = 0;
while (i < n) {
// Find first run
int left = i;
while (i + 1 < n && array[i] <= array[i + 1]) i++;
int mid = i;
i++;
if (i >= n) break;
// Find second run
while (i + 1 < n && array[i] <= array[i + 1]) i++;
int right = i;
i++;
// Merge both runs
merge(array, temp, left, mid, right);
merges++;
}
if (merges == 0) break;
}
}
private void merge(int[] array, int[] temp, int left, int mid, int right) {
System.arraycopy(array, left, temp, left, right - left + 1);
int i = left;
int j = mid + 1;
int k = left;
while (i <= mid && j <= right) {
if (temp[i] <= temp[j]) {
array[k++] = temp[i++];
} else {
array[k++] = temp[j++];
}
}
while (i <= mid) array[k++] = temp[i++];
while (j <= right) array[k++] = temp[j++];
}
public void mergeSort(int[] array) {
if (array == null || array.length <= 1) return;
int[] temp = new int[array.length];
mergeSortRecursive(array, temp, 0, array.length - 1);
}
private void mergeSortRecursive(int[] array, int[] temp, int left, int right) {
if (left >= right) return;
int mid = (left + right) >>> 1;
mergeSortRecursive(array, temp, left, mid);
mergeSortRecursive(array, temp, mid + 1, right);
mergeDescending(array, temp, left, mid, right);
}
private void mergeDescending(int[] array, int[] temp, int left, int mid, int right) {
System.arraycopy(array, left, temp, left, right - left + 1);
int i = left;
int j = mid + 1;
int k = left;
while (i <= mid && j <= right) {
if (temp[i] >= temp[j]) {
array[k++] = temp[i++];
} else {
array[k++] = temp[j++];
}
}
while (i <= mid) array[k++] = temp[i++];
while (j <= right) array[k++] = temp[j++];
}
public void insertionSort(int[] array) {
if (array == null || array.length <= 1) return;
for (int i = 1; i < array.length; i++) {
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] < key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
public void optimizedMergeSort(int[] array) {
if (array == null || array.length <= 1) return;
int[] temp = new int[array.length];
optimizedMergeSortRecursive(array, temp, 0, array.length - 1);
}
private void optimizedMergeSortRecursive(int[] array, int[] temp, int left, int right) {
if (left >= right) return;
if (right - left + 1 <= 10) {
insertionSortRange(array, left, right);
return;
}
int mid = (left + right) >>> 1;
optimizedMergeSortRecursive(array, temp, left, mid);
optimizedMergeSortRecursive(array, temp, mid + 1, right);
mergeDescending(array, temp, left, mid, right);
}
private void insertionSortRange(int[] array, int left, int right) {
for (int i = left + 1; i <= right; i++) {
int key = array[i];
int j = i - 1;
while (j >= left && array[j] < key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
public void testMergeSort() {
int[] array = new int[100];
Util util = new Util();
util.fillArrayRandom(array, 100);
Sorter mySorter = new Sorter();
mySorter.mergeSort(array);
util.printArray(array);
}
public void testOptimizedMergeSort() {
int[] array = new int[100];
Util util = new Util();
util.fillArrayRandom(array, 100);
Sorter mySorter = new Sorter();
mySorter.optimizedMergeSort(array);
util.printArray(array);
}
public static final void main(String[] args) {
int[] array = new int[100000000];
Util util = new Util();
util.fillArrayRandom(array, 10000000);
Sorter mySorter = new Sorter();
mySorter.sort(array);
}
}

View File

@@ -0,0 +1,153 @@
package util;
import org.jetbrains.annotations.NotNull;
/**
* Write a description of class Util here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class Util
{
public void printArray(int[] array)
{
for (int i = 0; i < array.length; ++i)
{
System.out.print(array[i]);
// Prüfen, ob wir nicht beim letzten Element sind.
// Auf das letzte Element sollte kein Komma folgen.
if (i < array.length - 1)
{
System.out.print(",");
}
}
// Nach Ausgabe brechen wir die Zeile um, damit folgende Ausgaben
// in einer neuen Zeile beginnen.
System.out.println();
}
public int randomInt(int upperLimit)
{
double random = Math.random(); // Zufallszahl zwischen 0.0 und 1.0 holen.
// Zufallszahl per Multiplikation auf 0.0-upperLimit skalieren
random = random * upperLimit;
// Nachkommastellen abschneiden und in Ganzzahlwert (int) umwandeln.
int result = (int) random;
return result;
}
public void fillArrayRandom(int[] array, int upperLimit)
{
for (int i = 0; i < array.length; ++i)
{
array[i] = randomInt(upperLimit);
}
}
public int[] concatArray(int[] array0, int[] array1)
{
int[] result = new int[array0.length + array1.length];
// Manuelles Kopieren mit Schleifen.
for (int i = 0; i < array0.length; ++i)
{
result[i] = array0[i];
}
for (int i = 0; i < array1.length; ++i)
{
// Um array0.length verschoben einfügen.
result[array0.length + i] = array1[i];
}
// Alternative Lösung unter Verwendung von System.arraycopy().
// System.arraycopy(array0, 0, result, 0, array0.length);
// System.arraycopy(array1, 0, result, array0.length, array1.length);
return result;
}
public int findMax(int[] array) {
int max = 0;
boolean found = false;
int loopRuns = 0;
for (int i = 0; !found && i < array.length; ++i) {
loopRuns++;
if (array[i] > max) {
max = array[i];
}
}
return loopRuns;
}
public int firstMatch(int[] array0, int[] array1) {
int loopRuns = 0;
for (int i = 0; i < array0.length; i++) {
for (int j = 0; j < array1.length; j++) {
loopRuns++;
if (array0[i] == array1[j]) {
return loopRuns;
}
}
}
return loopRuns;
}
public void fillArrayRandomRecursively(int[] array, int start, int upperLimit)
{
if (array.length - start <= 0)
{
return;
}
array[start] = randomInt(upperLimit);
fillArrayRandomRecursively(array, start + 1, upperLimit);
}
public void testRandomFillRecursively()
{
Util util = new Util();
int[] valueArray = new int[100];
util.fillArrayRandomRecursively(valueArray, 0, 100);
util.printArray(valueArray);
}
public int findMaxBySorting(int[] array){
Sorter sorter = new Sorter();
sorter.sort(array);
return array[array.length -1];
}
public Integer findValueBySorting(int @NotNull [] array, int value){
Sorter sorter = new Sorter();
sorter.sort(array);
int min = array[0];
int max = array[array.length -1];
if (value < min || value > max){
return null;
}
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
if (array[mid] == value) {
return mid;
} else if (array[mid] < value) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return null;
}
}

View File

@@ -0,0 +1,10 @@
package util;
public class test {
public static void main(String[] args){
Util util = new Util();
int[] arr = new int[1000];
util.fillArrayRandom(arr, 10000);
util.findValueBySorting(arr, 288);
}
}