Integração do GallopSearch: O (log (n) * log (i)) vez de O (n)
Fui em frente e implementei a sugestão de barba cinzenta nos comentários. Principalmente porque eu precisava de uma versão de missão crítica altamente eficiente desse código.
- O código usa um gallopSearch que é O (log (i)) em que i é a distância do índice atual em que o índice relevante existe.
- O código usa um binarySearch para após a pesquisa de galope identificar o intervalo apropriado. Como o galope limitou isso a um intervalo menor, o binarySearch resultante também é O (log (i))
- O galope e a mesclagem são executados para trás. Isso não parece crítico à missão, mas permite a fusão de matrizes. Se uma de suas matrizes tiver espaço suficiente para armazenar os valores dos resultados, você pode simplesmente usá-la como a matriz mesclada e a matriz de resultados. Você deve especificar o intervalo válido dentro da matriz nesse caso.
- Não requer alocação de memória nesse caso (grandes economias em operações críticas). Ele simplesmente garante que não substitua e não possa sobrescrever nenhum valor não processado (o que só pode ser feito ao contrário). De fato, você usa a mesma matriz para as entradas e os resultados. Não sofrerá efeitos negativos.
- Eu sempre usei Integer.compare () para que isso pudesse ser alterado para outros fins.
- Há alguma chance de eu ter enganado um pouco e não utilizado informações que eu já havia provado anteriormente. Como pesquisa binária em um intervalo de dois valores, para os quais um valor já foi verificado. Também pode haver uma maneira melhor de indicar o loop principal, o valor de inversão de c não seria necessário se eles fossem combinados em duas operações em sequência. Desde que você saiba que fará um e outro sempre. Há espaço para um pouco de polimento.
Essa deve ser a maneira mais eficiente de fazer isso, com complexidade de tempo de O (log (n) * log (i)) vez de O (n). E, no pior dos casos, complexidade do tempo de O (n). Se suas matrizes forem desajeitadas e tiverem longas cadeias de valores juntas, isso diminuirá qualquer outra maneira de fazê-lo; caso contrário, será apenas melhor que elas.
Ele possui dois valores de leitura nas extremidades da matriz mesclada e o valor de gravação na matriz de resultados. Depois de descobrir qual é o valor final menor, ele faz uma busca galopante nessa matriz. 1, 2, 4, 8, 16, 32, etc. Quando encontra o intervalo em que o valor de leitura da outra matriz é maior. Ele pesquisa binário nesse intervalo (corta o intervalo pela metade, pesquisa pela metade correta, repita até o valor único). Em seguida, o array copia esses valores na posição de gravação. Lembre-se de que a cópia é movida por necessidade, de modo que não possa sobrescrever os mesmos valores da matriz de leitura (o que significa que a matriz de gravação e a matriz de leitura podem ser as mesmas). Em seguida, ele executa a mesma operação para a outra matriz, que agora é conhecida como menor que o novo valor de leitura da outra 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;
}
Essa deve ser a maneira mais eficiente de fazer isso.
Algumas respostas tiveram uma capacidade de remoção duplicada. Isso exigirá um algoritmo O (n) porque você deve realmente comparar cada item. Então, aqui está um exemplo independente, a ser aplicado após o fato. Você não pode galopar várias entradas até o fim, se precisar olhar para todas elas, embora possa galopar pelas duplicatas, se tiver muitas delas.
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;
}
Atualização: resposta anterior, código não horrível, mas claramente inferior ao acima.
Outra hiper otimização desnecessária. Ele não apenas chama arraycopy para os bits finais, mas também para o começo. Processando qualquer não sobreposição introdutória em O (log (n)) por um binarySearch nos dados. O (log (n) + n) é O (n) e, em alguns casos, o efeito será bastante pronunciado, especialmente em situações em que não há sobreposição entre as matrizes mescladas.
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;
}