✅DAY22 回溯算法 | 77. 组合 | 216.组合总和III | 17.电话号码的字母组合

回溯算法

1. 原理:通过递归地构建解决方案,并在发现当前解不符合条件时,“回溯”到上一步,尝试其他可能的路径。

2. 应用:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等等

3. 经典结构

def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.append(路径)
        return

    for 选择 in 选择列表:
        # 做选择
        路径.append(选择)
        # 递归进入下一层
        backtrack(路径, 新的选择列表)
        # 撤销选择(回溯)
        路径.pop()

4. 优缺点:纯暴力,不高效

优点:可以求解所有解的组合,保证找到所有可能解。

缺点:在最坏情况下,回溯算法的时间复杂度会非常高,接近于指数级别,适合使用剪枝策略优化。

77. 组合

解题思路:组合是无序的,需剔除重复值

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        result = []
        
        def backtrack(start: int, path: List[int]):
            # 如果组合的长度达到了 k,收集当前组合
            if len(path) == k:
                result.append(list(path))  # 这里复制一份 path,并加入结果列表
                return
            
            # 从当前数字 start 开始,尝试添加数字到组合中
            for i in range(start, n + 1):
                # 做选择:将当前数字 i 添加到 path 中
                path.append(i)
                # 递归调用,构建后续组合
                backtrack(i + 1, path)
                # 回溯:撤销选择
                path.pop()
        
        # 从 1 开始构建组合
        backtrack(1, [])
        return result

剪枝优化:for loop里限制为 range(start, n - (k - len(path)) + 2)

        1. 还需要选 k - len(path) 个元素才能达到组合的长度 k

        2. 为了保证剩下的数字够用,最大位置应为 n - (k - len(path)) + 1

        3. range(start, end) 是左闭右开的区间,右边界设为 n - (k - len(path)) + 2,这样才能确保包含位置 n - (k - len(path)) + 1

216. 组合总和 III

示例 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
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        result= []

        def backtracking (currentSum, startIndex, path):
            if currentSum > n:
                return 
            if len(path) == k:
                if currentSum == n:
                    result.append(list(path))
            for i in range(startIndex, 9-(k-len(path))+2):
                currentSum += i
                path.append(i)
                backtracking(currentSum, i+1, path)
                currentSum -= i
                path.pop()
        
        backtracking(0, 1, [])
        return result

17. 电话号码的字母组合

class Solution:
    def __init__(self):
        # 数字到字母的映射表,索引对应数字的值
        self.letterMap = [
            "",     # 0 - 无映射
            "",     # 1 - 无映射
            "abc",  # 2 - 对应字母 abc
            "def",  # 3 - 对应字母 def
            "ghi",  # 4 - 对应字母 ghi
            "jkl",  # 5 - 对应字母 jkl
            "mno",  # 6 - 对应字母 mno
            "pqrs", # 7 - 对应字母 pqrs
            "tuv",  # 8 - 对应字母 tuv
            "wxyz"  # 9 - 对应字母 wxyz
        ]
        # 存储结果的列表
        self.result = []

    def getCombinations(self, digits, index, s):
        # 递归终止条件:当 index 到达 digits 的长度,说明组合已完成
        if index == len(digits):
            self.result.append(s)  # 将当前组合 s 加入结果列表
            return
        
        # 当前数字对应的字符
        digit = int(digits[index])  # 将当前字符转换为数字
        letters = self.letterMap[digit]  # 从映射表中获取数字对应的字母字符串
        
        # 遍历当前数字对应的所有字母
        for letter in letters:
            # 递归处理下一个数字,将当前字母加入组合
            self.getCombinations(digits, index + 1, s + letter)

    def letterCombinations(self, digits: str) -> List[str]:
        # 特殊情况处理:如果输入为空字符串,直接返回空结果
        if len(digits) == 0:
            return self.result
        
        # 开始递归,从第 0 个数字开始,初始组合字符串为空
        self.getCombinations(digits, 0, '')
        return self.result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值