Задача двух сумм — Leetcode

Постановка задачи

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

Примеры

Пример 1:

Input: nums = [7,2,13,11], target = 9
Output: [0,1]

Пример 2:

Input: nums = [7,3,5], target = 8
Output: [1,2]

Решения

Давайте сначала попытаемся понять постановку задачи. Здесь нам дан массив целочисленных элементов и целевая сумма. Мы должны написать алгоритм, который возвращает индексы двух элементов в этом массиве так, чтобы при сложении этих двух элементов он был равен заданной целевой сумме.

Например, в примере 1 [7,2,13,11] — это заданный массив и заданная целевая сумма = 9. Если мы посмотрим на данный массив, пара, которая добавляется к целевая сумма 9 равна (7,2), т.е. 7+2 = 9. Таким образом, наш алгоритм должен вернуть (0,1) в качестве результата, поскольку это индексы элементов 7 и 2 соответственно в данном массиве.

Аналогично для массива в примере 2 [7,3,5] выводится значение (1,2), поскольку это индексы элементов 3 и 5 соответственно, которые в сумме дают целевую сумму 8.

Примечание. Если таких пар несколько, нам нужно вернуть индексы первой пары слева.

В условии задачи указано, что мы можем возвращать индексы в любом порядке. Давайте разберемся в этом на первом примере. Выходные данные для этого примера — [0,1], поэтому, когда в условии задачи говорится, что мы можем возвращать индексы в любом порядке, это означает, что мы можем вернуть либо [0,1], либо [1,0] в качестве наших выходных данных, оба будут считаться правильными. То же самое и со вторым: мы можем вернуть либо [1,2], либо [2,1].

Решение 1: грубая сила

Простое решение этой проблемы — проверить каждую возможную пару, присутствующую в данном массиве.

Для данного входного массива nums нам необходимо выполнить следующие шаги:

  • Запустите два цикла и проверьте каждую комбинацию в данном массиве.
  • Зафиксируйте внешний цикл по определенному индексу и переместите внутренний цикл, чтобы получить все возможные пары. Внешний цикл выполняется от i=0 до i=n-2, а внутренний цикл — от j=i+1 до j=n-1.
  • На каждой итерации внутреннего цикла проверяйте, составляют ли суммы, представленные индексами внешнего и внутреннего цикла, целевую сумму.
  • Если nums[outerLoopIndex] + nums[innerLoopIndex] равно целевому значению, верните [outerLoopIndex ,innerLoopIndex] в качестве результата. В противном случае продолжите итерацию, чтобы проверить наличие следующей пары.
  • Повторяйте вышеуказанные шаги, пока не найдете комбинацию, соответствующую заданной цели.

Например, для массива [7,2,13,11] и целевой суммы 24 мы фиксируем внешний цикл с индексом i=0, то есть элементом 7, и проверяем его со всеми возможными значениями внутреннего цикла от j=i+1 до j=n-1, т.е. от индекса 1 до 3. Итак, на первой итерации внешнего цикла мы будем проверять следующую пару элементов: (7,2) (7,13) и (7,11). Теперь мы увеличиваем индекс i внешнего цикла на 1 и сверяем его с индексами от 2 до 3 (от i+1 до n-1) внутреннего цикла. Мы повторяем это до тех пор, пока не вернем правильный результат.

Примечание: здесь указан размер массива.

JavaScript

function twoSum(nums, target) {
    const n = nums.length;
    for (let i = 0; i < n - 1; i++) {
        for (let j = i + 1; j < n; j++) {
            if (nums[i] + nums[j] === target) {
                return [i, j];
            }
        }
    }
    return [];
}

Python3:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        n = len(nums)
        for i in range(n - 1):
            for j in range(i + 1, n):
                if nums[i] + nums[j] == target:
                    return [i, j]
        return []

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

Анализ сложности

  • Временная сложность: O(n²)
  • Космическая сложность: O(1)

Решение 2. Использование Hashmap

Эту задачу можно решить за линейное время. Идея состоит в том, чтобы использовать хеш-карту (объект в JavaScript) для хранения индексов уже видимых элементов. «Ключ» хэш-карты — это число в данном входном массиве (вы добавляете его в хэш-карту при посещении каждого элемента). «Значение» хэш-карты — это индекс числа в массиве, представленном ключом хэш-карты.

  • Создайте хэш-карту, которая принимает целочисленный тип данных в качестве ключа и значения.
  • Перебрать каждый элемент данного массива, начиная с первого элемента.
  • На каждой итерации проверяйте, присутствует ли необходимое число (требуемое число = целевая сумма — текущее число) в хэш-карте.
  • Если присутствует, верните в качестве результата [требуемый индекс номера, текущий индекс номера].
  • В противном случае добавьте текущий номер итерации в качестве ключа и его индекс в качестве значения в хэш-карту. Повторяйте это, пока не найдете результат

Моделирование

Предположим, вам предоставлен входной массив ниже и цель = 24.

Пусть currIdx будет переменной, представляющей текущий обрабатываемый элемент, а idxMap будет нашей картой индекса. Ячейки, отмеченные оранжевым цветом, обозначают обрабатываемый в данный момент элемент массива.

Вначале currIdx=0 и idxMap пусто, как показано на первом рисунке ниже. Далее мы проверяем, присутствует ли требуемое число = цель — текущее число в idxMap.

Требуемое число = 24–7 = 17 отсутствует в нашей хэш-карте, поэтому мы добавляем 7 в качестве ключа idxMap и 0 в качестве значения idxMap (0 — это индекс 7 в входной массив), как показано на рисунке 2 ниже.

Далее мы переходим ко второму элементу массива, увеличивая текущий индекс. Итак, currIdx=1 указывает на элемент 2 в массиве. Снова проверяем, присутствует ли нужное число в idxMap, нужного числа = 24–2 = 22 нет в нашем хеш-мапе, поэтому добавляем в хэш-карту 2 вместе с его индексом 1.

Увеличить текущий индекс currIdx=2, который является элементом 13 во входном массиве. Опять требуемое число = 24–13 = 11 отсутствует в нашей хэш-карте. Добавьте {13:2} в idxMap. То же самое показано на схеме ниже.

Наша хеш-карта теперь содержит 3 элемента 7, 2 и 13 вместе с их индексами. Снова мы увеличиваем currIdx, currIdx=3, что соответствует элементу 11.

Теперь требуемое число = 24–11 = 13 присутствует в idxMap (показано ячейкой, выделенной зеленым на втором рисунке ниже). Это означает, что мы нашли пару, сумма которой равна целевой сумме 24, то есть (11, 13). Поэтому в качестве результата мы возвращаем индексы 11 и 13. Индекс 11 — это не что иное, как currIdx, который равен 3, а индекс 13 можно найти из нашей хеш-карты, которая равна 2, поэтому в качестве результата мы возвращаем (3, 2) или (2, 3).

JavaScript:

function twoSum(nums, target) {
    const indexMap = new Map();

    for (let currIndex = 0; currIndex < nums.length; currIndex++) {
        const currNum = nums[currIndex];
        if (indexMap.has(target - currNum)) {
            return [indexMap.get(target - currNum), currIndex];
        }
        indexMap.set(currNum, currIndex);
    }

    return [];
}

Python3:

class Solution:
   def twoSum(self, nums: List[int], target: int) -> List[int]:
       seen = {}
       for i, value in enumerate(nums):
           remaining = target - nums[i]
           
           if remaining in seen:
               return [i, seen[remaining]]
            
           seen[value] = i

Анализ сложности

Временная сложность: O(n)

Пространственная сложность: O(n)

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

Кроме того, требуемое вспомогательное пространство равно O(n), поскольку мы храним элементы массива в хэш-карте, а в худшем случае нам придется хранить все значения в данном массиве в хэш-карте.

Это все, что касается этой статьи, спасибо, что нашли время, чтобы прочитать это. Если у вас есть какие-либо вопросы, дайте мне знать в разделе комментариев ниже, я буду более чем рад ответить.