力扣46.全排列、78.子集、17.电话号码的字母组合

零、回溯算法理论基础

参考:

回溯算法理论基础 | 代码随想录

回溯法解决的问题:

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

理解回溯:

回溯法解决的问题都可以抽象为树形结构,因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度。递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯三部曲:

1. 回溯函数返回值以及参数

  • 返回值一般为void

  • 参数不易一次性确定,所以一般先写逻辑,然后需要什么参数,就填什么参数。

    void backtracking(参数)

2. 回溯函数终止条件

一般搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。其伪代码为:

复制代码
if (终止条件) {
    存放结果;
    return;
}

3. 回溯搜索的遍历过程

回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。for循环横向遍历数组中的每一个元素,递归纵向遍历剩余元素,并根据递归终止条件来收获最终结果。

复制代码
void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

46.全排列

思路:

  1. 全排列是指数组中全部数字的所有排列方式,因此最终收获结果的条件是路径长度与数组本身长度相等。

  2. 整体思路如下图所示,用一个used数组来标记这个数字是否被使用,纵向递归,若某数字未被使用,则将其加入到路径并标记为已使用;横向循环,递归完成后进行回溯操作,把当前数字重新标记为未使用并移出路径。(看代码及注释可能更易理解)

注意:

使用 path[:] 而不是直接使用 path ,是为了避免引用问题,确保 result 中存储的是每个排列的独立副本,而不是对同一个列表的多次引用。

  1. 在 Python 中,列表是可变对象(mutable object)。当你将一个可变对象赋值给另一个变量时,这两个变量会指向同一个对象。

  2. 在回溯算法中,path 是一个动态变化的列表,它在递归过程中会被不断修改。如果直接将 path 添加到 result 中,那么 result 中存储的将是 path 的引用,而不是它的值。这意味着,当 path 被修改时,result 中对应的排列也会被修改。

  3. path[:] 是一种简单且高效的方法来创建列表的浅拷贝。

参考:

初学非常好理解的卡哥:

46.全排列 | 代码随想录

再刷发现写的也不错的题解,把我第一次刷的问题写了出来,path的拷贝问题

https://leetcode.cn/problems/permutations/solutions/9914/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw

代码:

python 复制代码
class Solution:
    def backtracking(self, nums, path, used, result):
        # 最终的排列收获条件
        if len(path) == len(nums):
            result.append(path[:])  # 注意要创建浅拷贝

        # 对数组循环,加入未被使用的数字
        for i in range(len(nums)):
            # 若已被使用,则跳过
            if used[i]:
                continue
            # 标记为已使用,并加入路径
            used[i] = True
            path.append(nums[i])
            # 递归
            self.backtracking(nums, path, used, result)
            # 回溯,当前轮次已收获完,重新将该数字标记为未使用,并弹出路径中的该元素
            used[i] = False
            path.pop()

    def permute(self, nums: List[int]) -> List[List[int]]:
        used = [False] * len(nums)
        result = []
        self.backtracking(nums, [], used, result)
        return result

78.子集

回溯三部曲:

1. 递归函数参数

全局变量数组path为子集收集元素,二维数组result存放子集组合。(也可以放到递归函数参数里)。递归函数参数需要startIndex,因为是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始。

2. 终止条件

如上图,剩余集合为空的时候,就是叶子节点。即,startIndex大于数组的长度,就终止了,因为没有元素可取了。

3. 单层搜索逻辑

从startIndex,加入该数字,然后回溯。

代码:

python 复制代码
class Solution:
    def backtracking(self, nums, index, path, result):
        result.append(path[:])  # 直接收获结果
        if index > len(nums):  # 其实可以不写,因为循环的index一直加到了len(nums)也会超,自动终止
            return

        for i in range(index, len(nums)):
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, result)
            path.pop()

    def subsets(self, nums: List[int]) -> List[List[int]]:
        result = []
        self.backtracking(nums, 0, [], result)
        return result

17.电话号码的字母组合

思路:

和之前的类似,只是多了一个数字和字母的对应关系,除此之外需要将数字字符转换为int类型。

代码:

python 复制代码
class Solution:
    def __init__(self):
        # 创建映射表,按顺序对应0,1,...,9
        self.letterMap = [
            "",
            "",
            "abc",
            "def",
            "ghi",
            "jkl",
            "mno",
            "pqrs",
            "tuv",
            "wxyz",
        ]
        self.result = []  # 收集结果
        self.s = ""  # 收集路径

    def backtracking(self, digits, index):
        if index == len(digits):
            self.result.append(self.s)
            return

        digit = int(digits[index])  # 字符转化为数字
        letters = self.letterMap[digit]  # 取对应字母用于遍历

        for i in range(len(letters)):
            self.s += letters[i]
            self.backtracking(digits, index + 1)
            self.s = self.s[:-1]  # 回溯

    def letterCombinations(self, digits: str) -> List[str]:
        if len(digits) == 0:
            return self.result

        self.backtracking(digits, 0)
        return self.result
相关推荐
子午2 小时前
【宠物识别系统】Python+深度学习+人工智能+算法模型+图像识别+TensorFlow+2026计算机毕设项目
人工智能·python·深度学习
好家伙VCC2 小时前
# 发散创新:用Python+Pandas构建高效BI数据清洗流水线在现代数据分析领域,**BI(商业智能)工具的核心竞
java·python·数据分析·pandas
菜鸡儿齐2 小时前
leetcode-最小覆盖子串
算法·leetcode·职场和发展
七夜zippoe2 小时前
TensorFlow 2.x深度实战:从Keras API到自定义训练循环
人工智能·python·tensorflow·keras
励ℳ2 小时前
Python环境操作完全指南
开发语言·python
ljxp12345682 小时前
二叉树中序遍历算法详解
python
查无此人byebye3 小时前
【超详细解读(GPU)】基于DiT的MNIST扩散模型(DDPM)完整实现
python·深度学习·nlp·transformer·多分类
赵谨言3 小时前
基于Python和ArcPy的不动产数据入库技术与运用
大数据·开发语言·经验分享·python
—Miss. Z—3 小时前
计算机软件资格考试—Python补充
开发语言·python