组合总和 III - 深度优先搜索(DFS)解题思路与代码实现

组合总和 III - 深度优先搜索(DFS)解题思路与代码实现

问题描述

给定一个整数 k 和一个目标数 n,要求从数字 1 到 9 中找到所有可能的组合,组合的长度为 k,并且所有数字之和为 n。每个数字最多使用一次,且解集不能包含重复的组合。组合的顺序不重要。

示例

示例 1:

输入:

k = 3, n = 7

输出:

[[1, 2, 4]]

解释:
1 + 2 + 4 = 7,只有这一种有效组合。

示例 2:

输入:

k = 3, n = 9

输出:

[[1, 2, 6], [1, 3, 5], [2, 3, 4]]

解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9

示例 3:

输入:

k = 4, n = 1

输出:

[]

解释:
[1, 9] 范围内使用 4 个不同的数字,得到的最小和是 1 + 2 + 3 + 4 = 10,而 10 > 1,因此没有有效组合。

解题思路

我们可以通过 深度优先搜索 (DFS) 的方式来解决这个问题。该方法的核心思想是使用回溯(Backtracking)来构建符合条件的数字组合。在实现过程中,主要考虑以下几点:

  1. 数字范围限制: 由于只能使用 1 到 9 的数字,每个数字最多使用一次,因此我们可以利用递归的方式逐步从 1 到 9 选择数字。
  2. 剪枝: 在递归过程中,如果剩余的目标值已经无法满足 k 个数字的最小和时,应该剪枝(提前结束递归)。
  3. 回溯: 每次选择一个数字进入下一层递归后,需要撤销当前的选择,返回上一层继续尝试其他数字。

算法步骤

  1. 深度优先搜索 (DFS): 从数字 1 开始递归,尝试组合符合条件的 k 个数字之和为 n
  2. 剪枝操作: 如果当前目标值 t 小于 0,或者当前目标值大于可以通过剩余 k 个数字的最大和,直接跳过该路径。
  3. 路径存储: 当路径长度等于 k,并且当前目标值为 0 时,将当前路径加入结果列表。

代码实现

from typing import List

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        ans = []  # 用来存放结果的列表
        path = []  # 用来存放当前路径的列表

        def dfs(i: int, t: int) -> None:
            # 剪枝:如果目标值小于0或无法通过剩余数字构成目标值,提前返回
            d = k - len(path)  # 剩余需要选择的数字个数
            if t < 0 or t > (i + i - d + 1) * d // 2:
                return
            if len(path) == k:  # 当路径长度达到k时,检查目标是否为0
                if t == 0:
                    ans.append(path.copy())
                return 
            
            # 从当前数字i开始,递归选择数字
            for j in range(i, d - 1, -1):
                path.append(j)
                dfs(j - 1, t - j)  # 递归调用,选择下一个数字
                path.pop()  # 回溯,撤销当前选择

        dfs(9, n)  # 从9开始,目标值为n
        return ans

代码解析

  1. 深度优先搜索 (DFS) 函数:
    函数 dfs(i, t) 表示从数字 i 开始,寻找和为 t 的组合,最多选 k 个数字。

    • 剪枝:
      if t < 0 or t > (i + i - d + 1) * d // 2:
      这个条件用于提前剪枝,避免无效的递归。具体来说,如果当前剩余目标 t 小于 0,或者大于剩余 d 个数字的最大可能和时,直接返回。

    • 递归选择数字:
      遍历 id-1 的数字,尝试将当前数字加入路径,并递归继续寻找剩余数字。

    • 回溯:
      选择一个数字后,需要将其从路径中移除,回到上一步,继续尝试其他数字。

  2. 递归基准条件:
    当路径长度等于 k,且目标值为 0 时,说明找到了一个符合要求的组合,加入结果列表。

  3. 回溯实现:
    在每一步递归选择后,使用 path.pop() 撤销选择,回到上一步,继续尝试其他可能的数字。

时间复杂度与空间复杂度

  • 时间复杂度: O(2^n),最坏情况下需要遍历所有可能的组合,其中 n 为数字的数量(在本题中为 9)。
  • 空间复杂度: O(k),每次递归深度最多为 k,因此需要的空间是路径长度 k

总结

通过深度优先搜索(DFS)和回溯,我们能够有效地找到所有符合条件的组合。在实现过程中,排序、剪枝和回溯操作是保证算法高效性的关键。希望这篇博客能帮助你理解如何利用回溯法解决组合问题。如果有任何问题或疑问,欢迎在评论区留言讨论!

你可能感兴趣的:(深度优先,算法)