返回

解题思路:使用回溯算法解决力扣 40 题:组合总和 II

后端

力扣 40 题:组合总和 II 的题目如下:

给定一个数组 candidates 和一个目标和 target,其中 candidates 中的元素可能存在重复。找出 candidates 中所有可以使数字和为 target 的唯一组合。

以下是不重复组合的示例:

candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8

输出:
[
  [1, 1, 6],
  [1, 2, 5],
  [1, 7],
  [2, 6]
]

本题可以使用回溯算法或动态规划算法解决。其中,回溯算法的思路如下:

  1. 排序 candidates 数组,以便于去重。
  2. 递归遍历 candidates 数组,在每个位置有两种选择:
    • 选择当前元素,将其加入组合中。
    • 不选择当前元素,继续递归遍历。
  3. 当组合中的数字和达到 target 时,将组合加入结果列表。
  4. 去除重复组合,只保留不重复的组合。

下面是使用回溯算法解决本题的 Python 代码:

def combinationSum2(candidates, target):
  """
  :type candidates: List[int]
  :type target: int
  :rtype: List[List[int]]
  """
  candidates.sort()  # 排序数组

  result = []

  def backtrack(start, combination, current_sum):
    if current_sum == target:
      result.append(combination.copy())
      return

    if current_sum > target:
      return

    for i in range(start, len(candidates)):
      # 去重
      if i > start and candidates[i] == candidates[i - 1]:
        continue

      combination.append(candidates[i])
      backtrack(i + 1, combination, current_sum + candidates[i])
      combination.pop()

  backtrack(0, [], 0)
  return result

本代码的时间复杂度为 O(2^n),其中 n 是 candidates 数组的长度。

除回溯算法外,还可以使用动态规划算法解决本题。具体思路如下:

  1. 定义一个二维数组 dp,其中 dp[i][j] = True 表示 candidates 数组的前 i 个元素可以组合成和为 j 的目标。
  2. 初始化 dp 数组,使得 dp[0][0] = True
  3. 遍历 candidates 数组和 dp 数组,当 dp[i - 1][j] = True 时,有以下两种情况:
    • dp[i][j] = True,表示不选择 candidates[i - 1].
    • dp[i][j + candidates[i - 1]] = True,表示选择 candidates[i - 1].
  4. 遍历 dp 数组,找到满足 dp[i][target] = Truei 值,即可得到组合。

下面是使用动态规划算法解决本题的 Python 代码:

def combinationSum2(candidates, target):
  """
  :type candidates: List[int]
  :type target: int
  :rtype: List[List[int]]
  """
  candidates.sort()  # 排序数组

  dp = [[False] * (target + 1) for _ in range(len(candidates) + 1)]
  dp[0][0] = True

  for i in range(1, len(candidates) + 1):
    for j in range(target + 1):
      if dp[i - 1][j] == True:
        dp[i][j] = True
        dp[i][j + candidates[i - 1]] = True

  result = []
  combination = []

  def backtrack(i, j):
    if j == 0:
      result.append(combination.copy())
      return

    if dp[i][j] == False:
      return

    if dp[i - 1][j] == True:
      backtrack(i - 1, j)

    if dp[i][j - candidates[i - 1]] == True:
      combination.append(candidates[i - 1])
      backtrack(i, j - candidates[i - 1])
      combination.pop()

  backtrack(len(candidates), target)
  return result

本代码的时间复杂度也为 O(2^n)。

总体来说,回溯算法和动态规划算法都可以解决本题,但回溯算法更直观,动态规划算法更省空间。