Найдите индексы пары элементов в массиве, чтобы они в сумме соответствовали целевому значению

Я хочу найти сумму target в массиве, добавляя целые числа, пока она не будет достигнута, а затем вернуть индексы, которые складываются в цель, используя потоки.

Например, если предоставленный массив — это {1, 2, 3, 4}, а target — это 4, метод должен распечатать массив int, состоящий из индексов {0,2}, но не делает этого.

Код выглядит следующим образом:

public static int[] twoSum(int[] numbers, int target) {
    IntStream.of(0, numbers.length - 1).boxed()
            .flatMap(i -> IntStream.range(i , numbers.length - 1).boxed()
            .filter(j -> numbers[j] + numbers[i] == target)
            .flatMap(j -> Stream.of(new int[]{i , j}, new int[] {j,i})))
            .forEach(num -> System.out.println(Arrays.toString(num)));

    return numbers;
}

public static void main(String[] args) {
    System.out.println(Arrays.toString(twoSum(new int[]{1,2,5,1}, 4)));
}

Не добавляя .boxed(). int[] b = Arrays.stream(numbers).filter((x -> num[finalI] + x == 4)).toArray(); А разве так не должно быть == target?

Elliott Frisch 06.05.2022 01:15

Да, это должна быть цель, я просто жестко запрограммировал цель какое-то время, но я также изменил ее на int[] b = Arrays.stream(numbers).filter((x -> num[finalI] + x == 4) ).toArray(); который работал. Но по какой-то причине возвращается пустой массив

bdunaway 06.05.2022 01:45

Приведите пример исходного массива и ожидаемого результата. Например, каким должен быть результат для массива 1, 4, 3, 0, 4, 1 и целевой суммы 4?

Alexander Ivanchenko 06.05.2022 01:56

Если бы целевая сумма была равна 4, она вернула бы индекс 0 и индекс 2, потому что сумма этих элементов равна 4.

bdunaway 06.05.2022 02:03

Я думал о создании Instream.range(0, number.length-1).boxed, который мог бы действовать как начальный цикл for, а затем о создании другого потока, который бы сравнивал все индексы с исходным потоком в штучной упаковке.

bdunaway 06.05.2022 02:05
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
5
116
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Для начала опишем возможные пути решения проблемы:

  • Подход грубой силы: перебрать исходный массив с помощью вложенного цикла или с помощью вложенного потока (напоминание: поток — это только средство итерации) и проверить для каждого элемента, есть ли аналог for в массиве, чтобы вместе они составляли заданную сумму. Временная сложность О (п ^ 2).
  • Так называемый подход с двумя указателями: отсортируйте заданный массив, определите две переменные, которые изначально указывают на первый и последний индексы, а затем переместите указатели. Этот алгоритм может быть реализован только в императивном стиле. Временная сложность — O (n журнал n) (потому что требуется сортировка), что намного лучше для вложенных циклов/потоков.
  • Создайте Map, который будет хранить все пары элементов, получающихся в результате, в целевую сумму. Алгоритм работает за линейное время На). Требуется только две итерации по исходному массиву.

В приведенном ниже решении представлена ​​потоковая реализация подхода, использующего Map.

В качестве первого шага нам нужно сгенерировать карта, который будет связывать значение, которое необходимо добавить к определенному элементу, чтобы получить сумму target (ключ) и индекс элемента массива (ценность).

Затем создайте поток по индексам данного массива, отфильтруйте первый элемент, который соответствует ключ в карта, и создайте на его основе массив двузначный.

Если результат не найден - вернуть пустой массив.

public static int[] twoSum(int[] numbers, int target) {
    Map<Integer, Integer> sumMap = getSumMap(numbers, target);
    
    return IntStream.range(0, numbers.length)
        .filter(i -> sumMap.containsKey(numbers[i]))
        .mapToObj(i -> new int[]{i, sumMap.get(numbers[i])})
        .findFirst()
        .orElse(new int[0]);
}

public static Map<Integer, Integer> getSumMap(int[] numbers, int target) {
    rreturn IntStream.range(0, numbers.length)
        .boxed()
        .collect(Collectors.toMap(
            i -> target - numbers[i], //  a key - dif between target sum and a current element
            Function.identity(),      //  a value - index of the current element
            (left, right) -> left));  //  resolving duplicates
}

main() - демо

public static void main(String[] args) {
    System.out.println(Arrays.toString(twoSum(new int[]{1, 4, 3, 0, 4, 1}, 4)));
}

Выход

[0, 2] // indices of the first pair of elements (1, 3) that can produce the sum of `4`

не могли бы вы объяснить назначение Function.identity() в getsummap?

bdunaway 06.05.2022 02:36

@bdunaway identity() возвращает ввод, который был передан в качестве аргумента без изменений. Это означает то же самое, что и следующая лямбда i -> i. Вместо этого часто используется Function.identity().

Alexander Ivanchenko 06.05.2022 02:41
Ответ принят как подходящий

Не уверен, но если вы используете именно этот код, возможно, это из-за опечатки после foreach. Вы инициируете ввод как nu, но печатаете его как num.

.forEach(num -> System.out.println(Arrays.toString(num)));

Возможно, замена этого на то, что у вас есть, решит проблему.

РЕДАКТИРОВАТЬ

Что касается дополнительных пояснений по комментариям, выяснилось, что это проблема с заданными значениями и не имеет ничего общего с API потока Java.

Вместо использования цикла for вы можете вложить два IntStreams для получения желаемого результата (насколько я понимаю). Это вернет все пары, сумма которых равна целевому значению. Это не включает элементы массива, которые добавили сами к себе равные цели. Таким образом, для цели 4[2, 2] в результате не включается

int[] arr1 = { 1, 2, 3, 4, 5, -2, -1, -3, -4, -5 };

int[][] array = twoSum(arr1, 4);

for (int[] a : array) {
    System.out.println(Arrays.toString(a));
}

отпечатки

[1, 3]
[5, -1]
  • Сначала передайте диапазон целых чисел от 0 до единицы меньше размера массива.
  • затем используйте boxed, чтобы преобразовать int в объект.
  • Затем вы flatMap (объединяете вложенные потоки в один) другой IntStream начиная с одного большего, чем предыдущий поток, но полной длины массива.
  • все еще внутри конструкции flatMap и используя значения IntStreams в качестве индексов для предоставленного массива, отфильтруйте значения, которые не суммируются с целью, и создайте массив из двух чисел, которые складываются (это также может быть заменены индексами массива, если это необходимо).
  • Затем верните этот массив массивов в качестве результата.
public static int[][] twoSum(int[] numbers, int target) {
    return IntStream.range(0, numbers.length - 1)
            .boxed()
            .flatMap(i -> IntStream.range(i + 1, numbers.length)
                    .filter(k -> numbers[i] + numbers[k] == target)
                    .mapToObj(k -> new int[] { numbers[i],numbers[k] }))
            .toArray(int[][]::new);
}

Другие вопросы по теме