Combinación de GallopSearch: O (log (n) * log (i)) en lugar de O (n)
Seguí adelante e implementé la sugerencia de barba gris en los comentarios. Principalmente porque necesitaba una versión de misión crítica altamente eficiente de este código.
- El código utiliza un gallopSearch que es O (log (i)) donde i es la distancia desde el índice actual que existe el índice relevante.
- El código utiliza una búsqueda binaria después de que la búsqueda de galope haya identificado el rango adecuado. Como gallop limitó esto a un rango más pequeño, la búsqueda binaria resultante también es O (log (i))
- El galope y la fusión se realizan al revés. Esto no parece crítico para la misión, pero permite la fusión de matrices. Si una de sus matrices tiene suficiente espacio para almacenar los valores de resultados, simplemente puede usarla como la matriz de fusión y la matriz de resultados. Debe especificar el rango válido dentro de la matriz en tal caso.
- No requiere asignación de memoria en ese caso (grandes ahorros en operaciones críticas). Simplemente se asegura de que no lo haga y no pueda sobrescribir ningún valor no procesado (que solo se puede hacer al revés). De hecho, utiliza la misma matriz para las entradas y los resultados. No sufrirá efectos nocivos.
- Usé constantemente Integer.compare (), por lo que esto podría cambiarse para otros fines.
- Hay alguna posibilidad de que me haya burlado un poco y no haya utilizado la información que he probado anteriormente. Tal como la búsqueda binaria en un rango de dos valores, para el cual un valor ya fue verificado. También podría haber una mejor manera de establecer el bucle principal, el valor de volteo c no sería necesario si se combinaran en dos operaciones en secuencia. Como sabes que harás uno y luego el otro cada vez. Hay espacio para un poco de esmalte.
Esta debería ser la forma más eficiente de hacerlo, con la complejidad temporal de O (log (n) * log (i)) en lugar de O (n). Y en el peor de los casos, la complejidad del tiempo de O (n). Si sus matrices son grumosas y tienen largas cadenas de valores juntas, esto eclipsará cualquier otra forma de hacerlo, de lo contrario, será mejor que ellas.
Tiene dos valores de lectura en los extremos de la matriz de fusión y el valor de escritura dentro de la matriz de resultados. Después de descubrir cuál es el valor final es menor, realiza una búsqueda de galope en esa matriz. 1, 2, 4, 8, 16, 32, etc. Cuando encuentra el rango donde el valor de lectura de la otra matriz es mayor. Realiza búsquedas binarias en ese rango (corta el rango a la mitad, busca la mitad correcta, repite hasta un solo valor). Luego, la matriz copia esos valores en la posición de escritura. Teniendo en cuenta que la copia, por necesidad, se mueve de modo que no pueda sobrescribir los mismos valores de la matriz de lectura (lo que significa que la matriz de escritura y la matriz de lectura pueden ser las mismas). Luego realiza la misma operación para la otra matriz que ahora se sabe que es menor que el nuevo valor de lectura de la otra matriz.
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;
}
Esta debería ser la forma más eficiente de hacerlo.
Algunas respuestas tenían una capacidad de eliminación duplicada. Eso requerirá un algoritmo O (n) porque en realidad debes comparar cada elemento. Así que aquí hay una aplicación independiente para eso, que se aplicará después del hecho. No puede galopar a través de múltiples entradas por completo si necesita verlas todas, aunque podría galopar a través de los duplicados, si tuviera muchos de ellos.
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;
}
Actualización: respuesta anterior, no es un código horrible, pero claramente inferior al anterior.
Otra innecesaria hiper-optimización. No solo invoca arraycopy para los bits finales, sino también para el comienzo. Procesar cualquier no solapamiento introductorio en O (log (n)) mediante una búsqueda binaria en los datos. O (log (n) + n) es O (n) y en algunos casos el efecto será bastante pronunciado, especialmente cuando no hay superposición entre las matrices de fusión.
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;
}