Взятый от введения до алгоритмов
Опишите Θ (n LG n) разовый алгоритм, который, учитывая набор S n целых чисел и другого целого числа x, определяет, существуют ли там два элемента в S, сумма которого точно x.
Это - мое лучшее решение, реализованное в Java до сих пор:
public static boolean test(int[] a, int val) {
mergeSort(a);
for (int i = 0; i < a.length - 1; ++i) {
int diff = (val >= a[i]) ? val - a[i] : a[i] - val;
if (Arrays.binarySearch(a, i, a.length, diff) >= 0) {
return true;
}
}
return false;
}
Теперь мой 1-й вопрос: действительно ли это - правильное решение? От моего понимания сортировка с объединением должна выполнить вид в O (n LG n), цикл должен взять O (n LG n) (n для повторения, умноженного на O (LG n) для двоичного поиска, приводящего к O (2n LG n), таким образом, это должно быть корректно.
Мой 2-й вопрос: Есть ли какие-либо лучшие решения? Действительно ли сортировка является важным массивом?
Ваше решение кажется хорошо. Да, вам нужно сортировать, потому что это предварительная необходимость для двоичного поиска. Вы можете сделать небольшую модификацию вашей логике следующим образом:
public static boolean test(int[] a, int val)
{
Arrays.sort(a);
int i = 0; // index of first element.
int j = a.length - 1; // index of last element.
while(i<j)
{
// check if the sum of elements at index i and j equals val, if yes we are done.
if(a[i]+a[j] == val)
return true;
// else if sum if more than val, decrease the sum.
else if(a[i]+a[j] > val)
j--;
// else if sum is less than val, increase the sum.
else
i++;
}
// failed to find any such pair..return false.
return false;
}
Ваш анализ правильный, и да, вы должны отсортировать массив или двоичный поиск не работает.
Невозможно, так как «X с Y» является анонимным определением в примере. Конечно, это не относится к «классу X, расширяющему Z с Y».
-121--3176635-Я думаю, что я заметил небольшую ошибку в вашей реализации, но тестирование должно быстро раскрыть эту ошибку.
Подход выглядит верным и достигнет желаемой производительности. Вы можете упростить его, заменив итеративный двоичный поиск сканированием через массив, фактически заменив двоичный поиск линейным поиском, который возобновляется там, где предыдущий линейный поиск остался:
int j = a.length - 1;
for (int i = 0; i < a.length; i++) {
while (a[i] + a[j] > val) {
j--;
}
if (a[i] + a[j] == val) {
// heureka!
}
}
Этот шаг является O (n). (Доказать, что это оставлено как упражнение для вас.) Конечно, весь алгоритм по-прежнему принимает O (n log n) для сортировки слиянием.
-121--1315270- Существует еще одно очень быстрое решение: Представьте, что вы должны решить эту задачу на Java для около 1 миллиарда целых чисел. Известно, что в Java целые числа переходят от -2 * * 31 + 1
к + 2 * * 31
.
Создайте массив с 2 * * 32
миллиардными битами (500 МБ, тривиально для современного оборудования).
Выполните итерацию над набором: если имеется целое число, установите соответствующий бит в 1.
O (n).
Повторите итерацию над набором: для каждого значения проверьте, установлен ли бит на «current val - x».
Если он у вас есть, вы возвращаете значение true.
Необходимо 500 МБ памяти.
Но это будет работать вокруг любого другого O (n log n) решения, если у вас есть, скажем, чтобы решить эту проблему с 1 миллиардом целых чисел.
O (n).
Вот раствор O (N) с использованием хеш-набора:
public static boolean test(int[] a, int val) {
Set<Integer> set = new HashSet<Integer>();
// Look for val/2 in the array
int c = 0;
for(int n : a) {
if(n*2 == val)
++c
}
if(c >= 2)
return true; // Yes! - Found more than one
// Now look pairs not including val/2
set.addAll(Arrays.asList(a));
for (int n : a) {
if(n*2 == val)
continue;
if(set.contains(val - n))
return true;
}
return false;
}
Невозможно, так как «X с Y» является анонимным определением в примере. Конечно, это не относится к «классу X, расширяющему Z с Y».
-121--3176635-Я думаю, что я заметил небольшую ошибку в вашей реализации, но тестирование должно быстро раскрыть эту ошибку.
Подход выглядит верным и достигнет желаемой производительности. Вы можете упростить его, заменив итеративный двоичный поиск сканированием через массив, фактически заменив двоичный поиск линейным поиском, который возобновляется там, где предыдущий линейный поиск остался:
int j = a.length - 1;
for (int i = 0; i < a.length; i++) {
while (a[i] + a[j] > val) {
j--;
}
if (a[i] + a[j] == val) {
// heureka!
}
}
Этот шаг является O (n). (Доказать, что это оставлено как упражнение для вас.) Конечно, весь алгоритм по-прежнему принимает O (n log n) для сортировки слиянием.
-121--1315270-Это правильно; ваш алгоритм будет запущен через O (n lg n) раз.
Есть лучшее решение: ваша логика вычисления diff неверна. Независимо от того, является ли a [i]
больше или меньше val
, все равно нужно, чтобы diff был val - a [i]
.
Если нам разрешено принимать, что вход можно отсортировать в O (N) с помощью Radix, я бы немного улучшился на растворе Криса:
Каждый из двух «указателей» движется строго вперед, поэтому общая сложность является O (N), предполагая, что сортировка RADIX является O (N) и что квадрат и сравнение o (1). Предположительно, кто наставил вопрос, предполагаемый эти предположения.
В ответ на комментарий вопросителя по другому ответу: если целые числа на входе не ограничены, то я не думаю, что это может быть сделано. Просто расчета квадрата целого числа требуется больше линейного времени (по крайней мере: ни один линейный алгоритм для умножения не известен), поэтому рассмотрим ввод размера N битов, состоящий из двух целых чисел размером N / 3
. и 2 * N / 3
бит. Тестирование того, является ли один квадрат другой, не может быть сделан в O (n). Я думаю. Я могу ошибаться.
Простое решение - это, после сортировки, перемещайте указатели с обоих концов массива, ищете пары, которые сумма на X. Если сумма слишком высока, уменьшайте правильный указатель. Если слишком низко, увеличивайте левый. Если указатели креста, ответ нет.
Я действительно думаю, что заметил небольшую ошибку в вашей реализации, но тестирование должно быстро ее обнаружить.
Подход выглядит корректно, и достигнет желаемой производительности. Вы можете упростить его, заменив итеративный двоичный поиск на сканирование через массив, фактически заменив двоичный поиск линейным, который возобновляется там, где предыдущий линейный поиск остановился:
int j = a.length - 1;
for (int i = 0; i < a.length; i++) {
while (a[i] + a[j] > val) {
j--;
}
if (a[i] + a[j] == val) {
// heureka!
}
}
Этот шаг - O(n). (Доказывая, что это оставлено как упражнение для вас.) Конечно, весь алгоритм все еще принимает O(n log n) для сортировки слияния.