LeetCode 39. Комбинации сумм – ссылки на списки Python дают разные результаты в бэктрекинге? [закрыто]

Вопрос или проблема

Я решаю leetcode 39. Комбинация суммы.:

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

То же самое число может быть выбрано из candidates неограниченное количество раз. Две комбинации являются уникальными, если частота хотя бы одного из выбранных чисел различна.

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

Ограничения

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • Все элементы candidates являются различными.
  • 1 <= target <= 500

Я запускаю четыре решения на python, и различия заключаются в использовании append() и pop():

def combination_sum_A(candidates, target):
    result = [] 
    def backtrack(remaining, combination, start):
        if remaining == 0:
            result.append(combination.copy()) 
            return
        if remaining < 0:
            return
        for i in range(start, len(candidates)):
            combination.append(candidates[i])
            backtrack(remaining - candidates[i], combination, i)
            combination.pop() 

    backtrack(target, [], 0)
    return result
        

def combination_sum_B(candidates, target):
    result = [] 
    def backtrack(remaining, combination, start):
        if remaining == 0:
            result.append(combination.copy()) 
            return
        if remaining < 0:
            return
        for i in range(start, len(candidates)):
            combination.append(candidates[i])
            backtrack(remaining - candidates[i], combination, i)
            #combination.pop() 
            combination = combination[:-1] 

    backtrack(target, [], 0)
    return result

def combination_sum_C(candidates, target):
    result = [] 
    def backtrack(remaining, combination, start):
        if remaining == 0:
            result.append(combination.copy()) 
            return
        if remaining < 0:
            return
        for i in range(start, len(candidates)):
            combination = combination+[candidates[i]]
            backtrack(remaining - candidates[i], combination, i)
            combination.pop() 
            

    backtrack(target, [], 0)
    return result

def combination_sum_D(candidates, target):
    result = [] 
    def backtrack(remaining, combination, start):
        if remaining == 0:
            result.append(combination.copy()) 
            return
        if remaining < 0:
            return
        for i in range(start, len(candidates)):
            combination = combination+[candidates[i]]
            backtrack(remaining - candidates[i], combination, i)
            #combination.pop() 
            combination = combination[:-1]
            

    backtrack(target, [], 0)
    return result

candidates = [2, 3, 6, 7]
target = 7
print(combination_sum_A(candidates, target))
print(combination_sum_B(candidates, target))
print(combination_sum_C(candidates, target))
print(combination_sum_D(candidates, target))

Только combination_sum_B() дает неправильные результаты. Я думал, что combination_sum_B() правильный, но он неправильный! Затем я подумал, что combination_sum_C() или combination_sum_D() могут быть неправильными, но на самом деле они оба правильные! Почему?

Ответ или решение

Почему разные реализации функции combination_sum ведут к различным результатам

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

Описание задачи

Задача на LeetCode 39, "Combination Sum", требует от нас найти все уникальные комбинации чисел из массива candidates, которые в сумме дают target. При этом каждое число может быть выбрано неограниченное количество раз.

Реализации функции

В приведенном вами коде имеются четыре реализации функции combination_sum, каждая из которых использует разные подходы к манипуляции списками.

  1. combination_sum_A: Здесь используется метод append() для добавления элемента в комбинацию и pop() для удаления последнего элемента. Этот подход корректен и позволяет перемещаться по текущему состоянию комбинации.

  2. combination_sum_B: В этом варианте вместо pop() используется combination = combination[:-1]. Эта конструкция не изменяет оригинальный список combination, а создает новый список без последнего элемента. Поэтому, когда функция возвращается к предыдущему состоянию, она не получает ожидаемое состояние списка. Это и есть причина, почему эта версия возвращает неправильные результаты.

  3. combination_sum_C: Как и в версии A, здесь мы создаем новый список с помощью combination+[candidates[i]], но отсутствует вызов pop(), который также не требуется, так как мы работаем с копиями списка на каждом шаге.

  4. combination_sum_D: Обеспечивает аналогичный подход к версии C, однако в конце цикла продолжает использовать combination = combination[:-1], что, как и во второй версии, приводит к неправильному поведению. Когда мы меняем combination, это не влияет на ссылку в стеке вызовов, и в результате мы работаем с измененной копией, которая не возвращает к исходному состоянию.

Резюме

Некорректная работа функции combination_sum_B возникает из-за неправильного управления ссылками на списки. В Python списки являются изменяемыми, и использование pop() непосредственно меняет состояние списка, тогда как создание нового списка с помощью среза или конкатенации (+) создает копию, не изменяющую оригинальный список.

В итоге, правильными подходами являются те, которые реализуют изменения непосредственно в оригинальном списке (версии A и C), в то время как версии B и D нарушают необходимую логику возврата к предыдущему состоянию из-за создания новых объектов списка.

Для правильного результата всегда обращайте внимание на то, как вы манипулируете изменяемыми объектами, и используйте методы, обеспечивающие ожидаемое поведение вашей программы.

Оцените материал
Добавить комментарий

Капча загружается...