Unione GallopSearch: O (log (n) * log (i)) anziché O (n)
Sono andato avanti e ho implementato il suggerimento di greybeard nei commenti. Soprattutto perché avevo bisogno di una versione mission-critical altamente efficiente di questo codice.
- Il codice utilizza un gallopSearch che è O (log (i)) dove i è la distanza dall'indice corrente in cui esiste l'indice rilevante.
- Il codice utilizza una ricerca binaria per dopo che la ricerca galoppo ha identificato l'intervallo corretto. Poiché galoppo ha limitato questo a un intervallo più piccolo, anche la ricerca binaria risultante è O (log (i))
- Il galoppo e l'unione vengono eseguiti all'indietro. Questo non sembra mission-critical, ma consente la fusione di array in atto. Se uno dei tuoi array ha spazio sufficiente per memorizzare i valori dei risultati, puoi semplicemente usarlo come array di fusione e array di risultati. In tal caso, è necessario specificare l'intervallo valido all'interno dell'array.
- In questo caso non richiede allocazione di memoria (grandi risparmi nelle operazioni critiche). Si assicura semplicemente che non lo fa e non può sovrascrivere alcun valore non elaborato (che può essere fatto solo al contrario). In effetti, si utilizza lo stesso array per entrambi gli input e i risultati. Non subirà effetti negativi.
- Ho usato costantemente Integer.compare () in modo che questo potesse essere disattivato per altri scopi.
- C'è qualche possibilità che potrei aver preso in giro un po 'e non utilizzare le informazioni che ho precedentemente dimostrato. Come la ricerca binaria in un intervallo di due valori, per i quali è già stato verificato un valore. Potrebbe esserci anche un modo migliore per dichiarare il loop principale, il valore di c invertito non sarebbe necessario se fossero combinati in due operazioni in sequenza. Dal momento che sai che farai uno poi l'altro ogni volta. C'è spazio per un po 'di smalto.
Questo dovrebbe essere il modo più efficiente per farlo, con la complessità temporale di O (log (n) * log (i)) piuttosto che O (n). E nel peggiore dei casi la complessità di O (n). Se le tue matrici sono ingombranti e hanno lunghe stringhe di valori insieme, questo ridurrà qualsiasi altro modo per farlo, altrimenti sarà solo meglio di loro.
Ha due valori di lettura alle estremità dell'array di fusione e il valore di scrittura all'interno dell'array di risultati. Dopo aver scoperto qual è il valore finale inferiore, esegue una ricerca al galoppo in quell'array. 1, 2, 4, 8, 16, 32, ecc. Quando trova l'intervallo in cui il valore letto dell'altro array è maggiore. Cerca binariamente in quell'intervallo (taglia l'intervallo a metà, cerca nella metà corretta, ripete fino al singolo valore). Quindi l'array copia quei valori nella posizione di scrittura. Tenendo presente che la copia viene, per necessità, spostata in modo tale da non poter sovrascrivere gli stessi valori dall'array di lettura (il che significa che l'array di scrittura e l'array di lettura possono essere gli stessi). Esegue quindi la stessa operazione per l'altro array che è ora noto essere inferiore al nuovo valore letto dell'altro array.
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;
}
Questo dovrebbe essere il modo più efficiente per farlo.
Alcune risposte avevano una capacità di rimozione duplicata. Ciò richiederà un algoritmo O (n) perché devi confrontare effettivamente ogni elemento. Quindi ecco un stand-alone per questo, da applicare dopo il fatto. Non puoi galoppare attraverso più voci fino in fondo se devi guardarle tutte, anche se potresti galoppare tra i duplicati, se ne avessi molti.
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;
}
Aggiornamento: risposta precedente, non un codice orribile ma chiaramente inferiore a quanto sopra.
Un'altra inutile iperottimizzazione. Invoca non solo arraycopy per i bit di fine, ma anche per l'inizio. Elaborazione di eventuali non sovrapposizioni introduttive in O (log (n)) mediante un binario Cerca nei dati. O (log (n) + n) è O (n) e in alcuni casi l'effetto sarà piuttosto pronunciato, specialmente cose come quelle in cui non vi è alcuna sovrapposizione tra gli array di fusione.
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;
}