Для данного массива целых чисел A 1 , A 2 , ..., A n , включая отрицательные и положительные числа, и еще одно целое число S. Теперь нам нужно найти три различных целых числа в массиве, сумма которых ближе всего к данному целому числу S Если существует более одного решения, любое из них в порядке.
Можно предположить, что все целые числа находятся в диапазоне int32_t, и при вычислении суммы не произойдет арифметического переполнения. S ничего особенного, но случайно выбранный номер.
Есть ли эффективный алгоритм, кроме перебора, чтобы найти три целых числа?
Ответы:
Ага; мы можем решить это за O (n 2 ) раз! Во-первых, учтите, что ваша проблема
P
может быть эквивалентно сформулирована несколько иначе, что устраняет необходимость в «целевом значении»:Обратите внимание , что вы можете перейти от этой версии проблемы
P'
сP
вычитанием вашего S / 3 от каждого элементаA
, но теперь вам не нужно целевое значение больше.Ясно, что если бы мы просто протестировали все возможные 3-кортежи, мы бы решили проблему в O (n 3 ) - это базовая линия грубой силы. Можно ли сделать лучше? Что если мы выберем кортежи несколько умнее?
Во-первых, мы потратим некоторое время на сортировку массива, что обойдется нам в первоначальный штраф O (n log n). Теперь мы выполняем этот алгоритм:
Этот алгоритм работает путем размещения трех указателей,
i
,j
, иk
в различных точках массива.i
начинается в начале и медленно продвигается к концу.k
указывает на самый последний элемент.j
указывает на то, гдеi
началось. Мы итеративно пытаемся суммировать элементы по их соответствующим индексам, и каждый раз происходит одно из следующего:j
ближе к концу, чтобы выбрать следующий по величине номер.k
ближе к началу, чтобы выбрать следующее наименьшее число.Для каждого
i
указателиj
иk
будут постепенно приближаться друг к другу. В конце концов они будут проходить друг друга, и в этот момент нам не нужно ничего для этого пробоватьi
, поскольку мы суммируем одни и те же элементы, просто в другом порядке. После этого мы попробуем следующееi
и повторим.В конце концов, мы либо исчерпаем полезные возможности, либо найдем решение. Вы можете видеть, что это O (n 2 ), поскольку мы выполняем внешний цикл O (n) раз и выполняем внутренний цикл O (n) раз. Это можно сделать субквадратично, если вы действительно придумаете, представив каждое целое число как битовый вектор и выполнив быстрое преобразование Фурье, но это выходит за рамки этого ответа.
Примечание. Поскольку это вопрос интервью, я немного обманываю: этот алгоритм позволяет выбирать один и тот же элемент несколько раз. То есть (-1, -1, 2) будет правильным решением, как (0, 0, 0). Он также находит только точные ответы, а не самый близкий ответ, как упоминается в заголовке. В качестве упражнения для читателя я дам вам понять, как заставить его работать только с отдельными элементами (но это очень простое изменение) и точными ответами (что также является простым изменением).
источник
конечно, это лучшее решение, потому что его легче читать и, следовательно, оно менее подвержено ошибкам. Единственная проблема в том, что нам нужно добавить несколько строк кода, чтобы избежать многократного выбора одного элемента.
Другое решение O (n ^ 2) (с использованием хэш-набора).
источник
s2
может быть уже выбранным элементом. Например, если массив есть0,1,2
иK
есть2
, ответа не должно быть. Я думаю, что ваш алгоритм будет выводить,0,1,1
что, очевидно, неверно.Решение Джона Феминеллы имеет ошибку.
На линии
Нам нужно проверить, все ли i, j, k различны. В противном случае, если мой целевой элемент
6
и если мой входной массив содержит{3,2,1,7,9,0,-4,6}
. Если я распечатаю кортежи с суммой 6, то я также получу0,0,6
вывод. Чтобы избежать этого, нам нужно изменить условие таким образом.источник
Как насчет чего-то вроде этого, который является O (n ^ 2)
Это находит, если сумма из 3 элементов в точности равна вашему числу. Если вы хотите ближайший, вы можете изменить его так, чтобы он запомнил наименьшую дельту (разницу между вашим числом текущего триплета) и в конце напечатал триплет, соответствующий наименьшей дельте.
источник
Обратите внимание, что у нас есть отсортированный массив. Это решение похоже на решение Джона только в том, что оно ищет сумму и не повторяет один и тот же элемент.
источник
a[r] + a[l] + a[i] - sum
. Примеритьarr = [-1, 2, 1, -4] sum = 1
.Вот код C ++:
источник
Очень простое решение N ^ 2 * logN: отсортируйте входной массив, затем просмотрите все пары A i , A j (время N ^ 2), и для каждой пары проверьте, находится ли (S - A i - A j ) в массиве ( время входа).
Другое решение O (S * N) использует классический подход динамического программирования .
Коротко:
Создайте двумерный массив V [4] [S + 1]. Заполните его так, чтобы:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 для любого i, V 1 [x] = 0 для всех остальных x
V [2] [A i + A j ] = 1, для любого i, j. V [2] [x] = 0 для всех остальных x
V [3] [сумма любых 3 элементов] = 1.
Чтобы заполнить его, переберите A i , для каждого перебирайте массив A i справа налево.
источник
Это может быть эффективно решено в O (n log (n)) следующим образом. Я даю решение, которое сообщает, равна ли сумма любых трех чисел данному числу.
источник
leftIndex
илиrightIndex
когда все элементы в середине либо строго меньше, либо больше, чем желаемое число. Но как насчет случая, когда бинарный поиск остановился где-то посередине? Вам нужно будет проверить обе ветви (гдеrightIndex--
иleftIndex++
). В своем решении вы просто игнорируете эту ситуацию. Но я не думаю, что есть способ преодолеть эту проблему.Сокращение: я думаю, что решение @John Feminella O (n2) является наиболее элегантным. Мы все еще можем уменьшить A [n] для поиска кортежа. Наблюдая A [k] так, чтобы все элементы находились в A [0] - A [k], когда наш массив поиска огромен, а SUM действительно малы.
[0] является минимальным: - отсортированный по возрастанию массив.
s = 2A [0] + A [k]: Учитывая s и A [], мы можем найти A [k], используя бинарный поиск по времени log (n).
источник
Вот программа в Java, которая является O (N ^ 2)
источник
Задача может быть решена в O (n ^ 2) путем расширения задачи 2-суммы с незначительными изменениями. A - вектор, содержащий элементы, а B - требуемая сумма.
int Solution :: threeSumClosest (vector & A, int B) {
источник
Вот код Python3
источник
Другое решение, которое проверяет и дает сбой рано
Я добавил несколько модульных тестов здесь: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Если набор использует слишком много места, я могу легко использовать java.util.BitSet, который будет использовать пространство O (n / w) .
источник
Программа, чтобы получить эти три элемента. Я только что отсортировал массив / список в первую очередь, и они обновляются
minCloseness
на основе каждого триплета.источник
Я сделал это в п ^ 3, мой псевдокод ниже;
// Создаем hashMap с ключом как Integer и значением как ArrayList // перебираем список с помощью цикла for, для каждого значения в списке перебираем снова, начиная со следующего значения;
// если сумма arr [i] и arr [j] меньше требуемой суммы, то существует вероятность найти третью цифру, поэтому сделайте другую для цикла
// в этом случае мы сейчас ищем третье значение; если сумма arr [i] и arr [j] и arr [k] является желаемой суммой, то добавьте их в HashMap, сделав arr [i] ключом, а затем добавив arr [j] и arr [k] в ArrayList в значении этого ключа
после этого у вас теперь есть словарь, в котором есть все записи, представляющие три значения, добавляя к желаемой сумме. Извлеките все эти записи, используя функции HashMap. Это сработало отлично.
источник