GallopSearch Merge: O (log (n) * log (i)) plutôt que O (n)
Je suis allé de l'avant et j'ai mis en œuvre la suggestion de barbe grise dans les commentaires. Surtout parce que j'avais besoin d'une version critique très efficace de ce code.
- Le code utilise un gallopSearch qui est O (log (i)) où i est la distance de l'index actuel où l'index pertinent existe.
- Le code utilise une recherche binaire après que la recherche au galop a identifié la plage appropriée. Puisque le galop a limité cela à une plage plus petite, la recherche binaire résultante est également O (log (i))
- Le galop et la fusion sont effectués à l'envers. Cela ne semble pas critique pour la mission, mais cela permet la fusion en place de tableaux. Si l'un de vos tableaux a suffisamment d'espace pour stocker les valeurs des résultats, vous pouvez simplement l'utiliser comme tableau de fusion et tableau de résultats. Vous devez spécifier la plage valide dans le tableau dans un tel cas.
- Il ne nécessite pas d'allocation de mémoire dans ce cas (grandes économies dans les opérations critiques). Il s'assure simplement qu'il n'écrase pas et ne peut pas écraser les valeurs non traitées (ce qui ne peut être fait qu'à l'envers). En fait, vous utilisez le même tableau pour les entrées et les résultats. Il ne souffrira aucun effet néfaste.
- J'ai toujours utilisé Integer.compare () afin que cela puisse être changé à d'autres fins.
- Il y a une chance que j'aie un peu gaffé et que je n'utilise pas les informations que j'ai prouvées précédemment. Comme la recherche binaire dans une plage de deux valeurs, pour laquelle une valeur a déjà été vérifiée. Il pourrait également y avoir une meilleure façon de déclarer la boucle principale, la valeur c inversée ne serait pas nécessaire si elles étaient combinées en deux opérations en séquence. Puisque vous savez que vous en ferez un puis l'autre à chaque fois. Il y a de la place pour du vernis.
Cela devrait être le moyen le plus efficace de le faire, avec une complexité temporelle de O (log (n) * log (i)) plutôt que O (n). Et dans le pire des cas, la complexité temporelle de O (n). Si vos tableaux sont groupés et ont de longues chaînes de valeurs ensemble, cela éclipsera toute autre façon de le faire, sinon ce sera juste mieux qu'eux.
Il a deux valeurs de lecture aux extrémités du tableau de fusion et la valeur d'écriture dans le tableau de résultats. Après avoir découvert quelle est la valeur finale la moins élevée, il effectue une recherche galopante dans ce tableau. 1, 2, 4, 8, 16, 32, etc. Lorsqu'il trouve la plage où la valeur lue de l'autre tableau est plus grande. Il recherche binaire dans cette plage (coupe la plage de moitié, recherche la moitié correcte, répète jusqu'à une valeur unique). Ensuite, le tableau copie ces valeurs dans la position d'écriture. En gardant à l'esprit que la copie est, par nécessité, déplacée de telle sorte qu'elle ne puisse pas écraser les mêmes valeurs de l'un ou l'autre des tableaux de lecture (ce qui signifie que le tableau d'écriture et le tableau de lecture peuvent être identiques). Il effectue ensuite la même opération pour l'autre tableau qui est maintenant connu pour être inférieur à la nouvelle valeur de lecture de l'autre tableau.
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;
}
Cela devrait être le moyen le plus efficace de le faire.
Certaines réponses avaient une capacité de suppression en double. Cela nécessitera un algorithme O (n) car vous devez en fait comparer chaque élément. Voici donc une solution autonome pour cela, à appliquer après coup. Vous ne pouvez pas galoper à travers plusieurs entrées si vous avez besoin de les regarder toutes, bien que vous puissiez galoper à travers les doublons, si vous en aviez beaucoup.
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;
}
Mise à jour: réponse précédente, code pas horrible mais clairement inférieur à ce qui précède.
Encore une hyper-optimisation inutile. Il appelle non seulement arraycopy pour les bits de fin, mais aussi pour le début. Traitement de tout non-chevauchement introductif dans O (log (n)) par une recherche binaire dans les données. O (log (n) + n) est O (n) et dans certains cas, l'effet sera assez prononcé, en particulier dans les cas où il n'y a pas du tout de chevauchement entre les tableaux de fusion.
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;
}