Вопрос или проблема
Я решаю 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
, каждая из которых использует разные подходы к манипуляции списками.
-
combination_sum_A
: Здесь используется методappend()
для добавления элемента в комбинацию иpop()
для удаления последнего элемента. Этот подход корректен и позволяет перемещаться по текущему состоянию комбинации. -
combination_sum_B
: В этом варианте вместоpop()
используетсяcombination = combination[:-1]
. Эта конструкция не изменяет оригинальный списокcombination
, а создает новый список без последнего элемента. Поэтому, когда функция возвращается к предыдущему состоянию, она не получает ожидаемое состояние списка. Это и есть причина, почему эта версия возвращает неправильные результаты. -
combination_sum_C
: Как и в версии A, здесь мы создаем новый список с помощьюcombination+[candidates[i]]
, но отсутствует вызовpop()
, который также не требуется, так как мы работаем с копиями списка на каждом шаге. -
combination_sum_D
: Обеспечивает аналогичный подход к версии C, однако в конце цикла продолжает использоватьcombination = combination[:-1]
, что, как и во второй версии, приводит к неправильному поведению. Когда мы меняемcombination
, это не влияет на ссылку в стеке вызовов, и в результате мы работаем с измененной копией, которая не возвращает к исходному состоянию.
Резюме
Некорректная работа функции combination_sum_B
возникает из-за неправильного управления ссылками на списки. В Python списки являются изменяемыми, и использование pop()
непосредственно меняет состояние списка, тогда как создание нового списка с помощью среза или конкатенации (+
) создает копию, не изменяющую оригинальный список.
В итоге, правильными подходами являются те, которые реализуют изменения непосредственно в оригинальном списке (версии A и C), в то время как версии B и D нарушают необходимую логику возврата к предыдущему состоянию из-за создания новых объектов списка.
Для правильного результата всегда обращайте внимание на то, как вы манипулируете изменяемыми объектами, и используйте методы, обеспечивающие ожидаемое поведение вашей программы.