GallopSearch Merge: O (log (n) * log (i)), а не O (n)
Я пошел дальше и реализовал предложение серой бороды в комментариях. Главным образом потому, что мне нужна высокоэффективная критическая версия этого кода.
- Код использует gallopSearch, который равен O (log (i)), где i - это расстояние от текущего индекса, для которого существует соответствующий индекс.
- В коде используется двоичный поиск после того, как поиск галопом определил правильный диапазон. Поскольку галоп ограничил это меньшим диапазоном, получаемый двоичный поиск также равен O (log (i))
- Галоп и слияние выполняются задом наперед. Это не кажется критически важным, но позволяет объединять массивы. Если один из ваших массивов имеют достаточно мест для хранения значений результатов, вы можете просто использовать его в качестве присоединяемого массива и массива результатов. В этом случае вы должны указать допустимый диапазон в массиве.
- В этом случае не требуется выделение памяти (большая экономия при критических операциях). Он просто проверяет, не перезаписывает ли необработанные значения (что может быть сделано только в обратном направлении). Фактически вы используете один и тот же массив как для входных данных, так и для результатов. Он не будет страдать от вредных последствий
- Я последовательно использовал Integer.compare (), чтобы это можно было отключить для других целей.
- Есть некоторый шанс, что я мог бы немного обмануть и не использовать информацию, которую я ранее доказал. Например, двоичный поиск в диапазоне двух значений, для которого уже проверено одно значение. Также мог бы быть лучший способ сформулировать основной цикл, если бы значение c flipping c не потребовалось бы, если бы они были последовательно объединены в две операции. Поскольку вы знаете, что будете делать одно, то другое каждый раз. Есть место для полировки.
Это должен быть наиболее эффективный способ сделать это с временной сложностью O (log (n) * log (i)), а не O (n). И в худшем случае временная сложность O (n). Если ваши массивы клочковатые и содержат длинные цепочки значений, это затмит любой другой способ сделать это, иначе это будет просто лучше, чем они.
Он имеет два значения чтения на концах массива объединения и значение записи в массиве результатов. После того, как выясняется, какое из конечных значений меньше, он делает скачок в этом массиве. 1, 2, 4, 8, 16, 32 и т. Д. Когда он находит диапазон, в котором значение чтения другого массива больше. Он выполняет двоичный поиск в этом диапазоне (сокращает диапазон пополам, ищет правильную половину, повторяет до одного значения). Затем массив копирует эти значения в позицию записи. Помните, что копия по необходимости перемещается так, что не может перезаписывать одинаковые значения из любого массива чтения (что означает, что массив записи и массив чтения могут быть одинаковыми). Затем он выполняет ту же операцию для другого массива, который, как теперь известно, меньше нового значения чтения другого массива.
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;
}
Это должен быть самый эффективный способ сделать это.
У некоторых ответов была возможность удаления дубликатов. Это потребует алгоритма O (n), потому что вы должны сравнить каждый элемент. Так что вот для этого отдельная вещь, которая будет применяться после факта. Вы не можете просматривать несколько записей, если вам нужно просмотреть все из них, хотя вы могли бы просматривать дубликаты, если их было много.
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;
}
Обновление: предыдущий ответ, не ужасный код, но явно уступает вышеуказанному.
Еще одна ненужная гипероптимизация. Он не только запускает arraycopy для конечных битов, но и для начала. Обработка любого вводного непересекающегося в O (log (n)) двоичного поиска в данных. O (log (n) + n) - это O (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;
}