Злиття GallopSearch: O (log (n) * log (i)), а не O (n)
Я пішов вперед і реалізував пропозицію сірої бороди в коментарях. Переважно тому, що мені потрібна була високоефективна критична версія місії для цього коду.
- Код використовує галоп-пошук, який є O (log (i)), де i - відстань від поточного індексу, відповідного індексу існує.
- У коді використовується binarySearch, після того як пошук галопом визначив правильний, діапазон. Оскільки галоп обмежив це меншим діапазоном, то отриманий binarySearch також є O (log (i))
- Галоп і злиття виконуються назад. Це не здається важливим для місії, але це дозволяє об'єднати масиви. Якщо в одному з ваших масивів є достатньо місця для зберігання значень результатів, ви можете просто використовувати його як масив об'єднання та масив результатів. У такому випадку потрібно вказати допустимий діапазон у масиві.
- У цьому випадку він не потребує розподілу пам'яті (велика економія на критичних операціях). Він просто переконує, що він не може і не може перезаписати будь-які необроблені значення (що можна зробити лише назад). Насправді ви використовуєте один і той же масив як для входів, так і для результатів. Він не зазнає жодних негативних наслідків.
- Я послідовно використовував Integer.compare (), щоб це можна було вимкнути для інших цілей.
- Є певний шанс, що я, можливо, трохи поблукав і не використав інформацію, яку я раніше довів. Наприклад, бінарний пошук у діапазоні двох значень, для яких одне значення вже було перевірено. Також може бути кращий спосіб констатувати основний цикл, значення гортання 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;
}
Оновлення: попередня відповідь, не жахливий код, але явно поступається вище.
Ще одна непотрібна гіпероптимізація. Він не тільки викликає масив масиву для кінцевих бітів, але і для початку. Обробка будь-якого вступного неперекриття в O (log (n)) за допомогою binarySearch у дані. 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;
}