GallopSearch Merge: O (log (n) * log (i)) statt O (n)
Ich ging voran und implementierte den Vorschlag von Greybeard in den Kommentaren. Vor allem, weil ich eine hocheffiziente geschäftskritische Version dieses Codes brauchte.
- Der Code verwendet eine GallopSearch, die O (log (i)) ist, wobei i der Abstand zum aktuellen Index ist, für den der relevante Index existiert.
- Der Code verwendet eine binarySearch, nachdem die Galoppsuche den richtigen Bereich identifiziert hat. Da Galopp dies auf einen kleineren Bereich begrenzt hat, ist die resultierende binäre Suche auch O (log (i))
- Der Galopp und das Zusammenführen werden rückwärts ausgeführt. Dies scheint nicht geschäftskritisch zu sein, ermöglicht jedoch das Zusammenführen von Arrays. Wenn in einem Ihrer Arrays genügend Platz zum Speichern der Ergebniswerte vorhanden ist, können Sie es einfach als Zusammenführungsarray und Ergebnisarray verwenden. In einem solchen Fall müssen Sie den gültigen Bereich innerhalb des Arrays angeben.
- In diesem Fall ist keine Speicherzuweisung erforderlich (große Einsparungen bei kritischen Vorgängen). Es stellt einfach sicher, dass nicht verarbeitete Werte nicht überschrieben werden (was nur rückwärts möglich ist). Tatsächlich verwenden Sie für beide Eingaben und Ergebnisse dasselbe Array. Es wird keine negativen Auswirkungen haben.
- Ich habe konsequent Integer.compare () verwendet, damit dies für andere Zwecke ausgeschaltet werden kann.
- Es besteht die Möglichkeit, dass ich ein wenig vermasselt und Informationen, die ich zuvor bewiesen habe, nicht verwendet habe. Zum Beispiel die binäre Suche in einem Bereich von zwei Werten, für die bereits ein Wert geprüft wurde. Es könnte auch eine bessere Möglichkeit geben, die Hauptschleife anzugeben. Der Flip-C-Wert wäre nicht erforderlich, wenn sie nacheinander zu zwei Operationen kombiniert würden. Da Sie wissen, dass Sie jedes Mal das eine und das andere tun werden. Es gibt Platz für etwas Politur.
Dies sollte der effizienteste Weg sein, mit einer zeitlichen Komplexität von O (log (n) * log (i)) anstelle von O (n). Und Worst-Case-Zeitkomplexität von O (n). Wenn Ihre Arrays klumpig sind und lange Werteketten zusammen haben, wird dies jede andere Möglichkeit in den Schatten stellen, andernfalls ist es einfach besser als sie.
Es hat zwei Lesewerte am Ende des Zusammenführungsarrays und den Schreibwert innerhalb des Ergebnisarrays. Nachdem herausgefunden wurde, welcher Endwert geringer ist, wird eine Galoppsuche in diesem Array durchgeführt. 1, 2, 4, 8, 16, 32 usw. Wenn der Bereich gefunden wird, in dem der Lesewert des anderen Arrays größer ist. Es sucht binär in diesen Bereich (halbiert den Bereich, sucht die richtige Hälfte, wiederholt bis zum Einzelwert). Dann kopiert es Array diese Werte in die Schreibposition. Beachten Sie, dass die Kopie notwendigerweise so verschoben wird, dass sie nicht dieselben Werte aus beiden Lesearrays überschreiben kann (was bedeutet, dass das Schreibarray und das Lesearray identisch sein können). Es führt dann dieselbe Operation für das andere Array aus, von dem jetzt bekannt ist, dass es kleiner als der neue Lesewert des anderen Arrays ist.
static public int gallopSearch(int current, int[] array, int v) {
int d = 1;
int seek = current - d;
int prevIteration = seek;
while (seek > 0) {
if (Integer.compare(array[seek], v) <= 0) {
break;
}
prevIteration = seek;
d <<= 1;
seek = current - d;
if (seek < 0) {
seek = 0;
}
}
if (prevIteration != seek) {
seek = binarySearch(array, seek, prevIteration, v);
seek = seek >= 0 ? seek : ~seek;
}
return seek;
}
static public int binarySearch(int[] list, int fromIndex, int toIndex, int v) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = list[mid];
int cmp = Integer.compare(midVal, v);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return mid;// key found
}
}
return -(low + 1);// key not found.
}
static public int[] sortedArrayMerge(int[] a, int[] b) {
return sortedArrayMerge(null, a, a.length, b, b.length);
}
static public int[] sortedArrayMerge(int[] results, int[] a, int aRead, int b[], int bRead) {
int write = aRead + bRead, length, gallopPos;
if ((results == null) || (results.length < write)) {
results = new int[write];
}
if (aRead > 0 && bRead > 0) {
int c = Integer.compare(a[aRead - 1], b[bRead - 1]);
while (aRead > 0 && bRead > 0) {
switch (c) {
default:
gallopPos = gallopSearch(aRead, a, b[bRead-1]);
length = (aRead - gallopPos);
write -= length;
aRead = gallopPos;
System.arraycopy(a, gallopPos--, results, write, length);
c = -1;
break;
case -1:
gallopPos = gallopSearch(bRead, b, a[aRead-1]);
length = (bRead - gallopPos);
write -= length;
bRead = gallopPos;
System.arraycopy(b, gallopPos--, results, write, length);
c = 1;
break;
}
}
}
if (bRead > 0) {
if (b != results) {
System.arraycopy(b, 0, results, 0, bRead);
}
} else if (aRead > 0) {
if (a != results) {
System.arraycopy(a, 0, results, 0, aRead);
}
}
return results;
}
Dies sollte der effizienteste Weg sein, dies zu tun.
Einige Antworten hatten eine doppelte Entfernungsfähigkeit. Dies erfordert einen O (n) -Algorithmus, da Sie tatsächlich jedes Element vergleichen müssen. Hier ist also eine eigenständige Anwendung, die nachträglich angewendet werden kann. Sie können nicht durch mehrere Einträge galoppieren, wenn Sie sich alle ansehen müssen, obwohl Sie durch die Duplikate galoppieren könnten, wenn Sie viele davon hätten.
static public int removeDuplicates(int[] list, int size) {
int write = 1;
for (int read = 1; read < size; read++) {
if (list[read] == list[read - 1]) {
continue;
}
list[write++] = list[read];
}
return write;
}
Update: Vorherige Antwort, kein schrecklicher Code, aber deutlich schlechter als oben.
Eine weitere unnötige Hyperoptimierung. Es ruft nicht nur Arraycopy für die Endbits auf, sondern auch für den Anfang. Verarbeiten einer einleitenden Nichtüberlappung in O (log (n)) durch eine binäre Suche in den Daten. O (log (n) + n) ist O (n) und in einigen Fällen ist der Effekt ziemlich ausgeprägt, insbesondere wenn es überhaupt keine Überlappung zwischen den zusammengeführten Arrays gibt.
private static int binarySearch(int[] array, int low, int high, int v) {
high = high - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = array[mid];
if (midVal > v)
low = mid + 1;
else if (midVal < v)
high = mid - 1;
else
return mid; // key found
}
return low;//traditionally, -(low + 1); // key not found.
}
private static int[] sortedArrayMerge(int a[], int b[]) {
int result[] = new int[a.length + b.length];
int k, i = 0, j = 0;
if (a[0] > b[0]) {
k = i = binarySearch(b, 0, b.length, a[0]);
System.arraycopy(b, 0, result, 0, i);
} else {
k = j = binarySearch(a, 0, a.length, b[0]);
System.arraycopy(a, 0, result, 0, j);
}
while (i < a.length && j < b.length) {
result[k++] = (a[i] < b[j]) ? a[i++] : b[j++];
}
if (j < b.length) {
System.arraycopy(b, j, result, k, (b.length - j));
} else {
System.arraycopy(a, i, result, k, (a.length - i));
}
return result;
}