目录

从零讲透DFS-深度优先搜索-2(排序与组合)

一、核心知识点清单

1. 排列组合问题分类
  • 排列问题(顺序相关):如全排列(LeetCode 46)。

  • 组合问题(顺序无关):如子集(LeetCode 78)。

  • 关键区别:是否考虑元素的顺序。

2. 回溯模板强化
python 复制代码
def backtrack(path, choices):
    if 满足终止条件:
        记录结果
        return
    for 选择 in choices:
        if 选择不合法:  # 剪枝
            continue
        path.append(选择)          # 做选择
        backtrack(path, 新choices) # 递归
        path.pop()                # 撤销选择(回溯)
3. 去重技巧
  • 排序+跳过重复元素:适用于输入含重复数字的情况(如LeetCode 47)。

  • 示例代码

    python 复制代码
    nums.sort()  # 先排序
    for i in range(len(nums)):
        if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
            continue  # 跳过重复分支
4. 剪枝优化场景
  • 提前终止:当路径不可能满足条件时(如组合总和超过目标值)。

  • 跳过无效分支:如全排列中已使用的数字不再选择。

5. 视频教程

6. 图文教程


二、学习安排(2小时)

第1个30分钟:理解排列与组合的区别

对比练习

  • 全排列([1,2,3] → 6种顺序不同的解)。

    \[1,2,3\],\[1,3,2\],\[2,1,3\],\[2,3,1\],\[3,1,2\],\[3,2,1\]

  • 子集([1,2,3] → 8种包含不同元素的子集)。

\[空集\],\[1\],\[2\],\[3\],\[1,2\],\[1,3\],\[2,3\],\[1,2,3\]

显然**[1,2] [2,1]是两个排列,但是为同一个组合。**

画决策树

  • 用纸笔画出输入为[1,2]时的全排列和子集决策树,体会回溯过程。
第2个30分钟:全排列代码实现
  1. 题目:LeetCode 46. 全排列(无重复数字)。

    • 代码框架

      python 复制代码
      def permute(nums):
          def backtrack(path):
              if len(path) == len(nums):
                  res.append(path.copy())
                  return
              for num in nums:
                  if num in path:  # 剪枝:已使用的数字跳过
                      continue
                  path.append(num)
                  backtrack(path)
                  path.pop()
          res = []
          backtrack([])
          return res
    • 关键点 :用path记录已选数字,通过if num in path剪枝。

值得注意的是上面的代码在力扣的运行中频繁报错AttributeError: 'list' object has no attribute 'copy', 这不禁联想到相同情况之前引用split报错,这里的原因是访问类型的不匹配,就好比split中我们试图将字符串分隔,但可能我们要操作的对象并不是字符串类型,而是列表类型。后面我将这句copy改成append("".join(path))也是报错。这里代码尝试把整数列表path连接成字符串。不过join方法要求传入的可迭代对象元素是字符串,而path中的元素是整数,所以会引发 TypeError

所以可以得出这一块我们正在操作的是列表类型下的整数元素,最终将代码改成append(path[:]),即为正确答案。

这里不断修改类型是因为参考的是力扣的题目,有输入输出类型的限制。

:type nums: List[int]

:rtype: List[List[int]]

第3个30分钟:子集问题与去重
  1. 题目:LeetCode 78. 子集。

    • 代码框架

      python 复制代码
      def subsets(nums):
          def backtrack(start, path):
              res.append(path[:])  # 所有节点均记录结果
              for i in range(start, len(nums)):  # 避免重复选择
                  path.append(nums[i])
                  backtrack(i + 1, path)  # 从i+1开始选
                  path.pop()
          res = []
          backtrack(0, [])
          return res
    • 与全排列的区别 :通过start参数控制选择范围,避免顺序不同但元素相同的重复解。

第4个30分钟:全代码去重问题
  1. 题目:LeetCode 47. 全排列2。

    • 剪枝技巧:标记已使用过的数组、相同数字按顺序使用避免重复。

    • 代码片段

      python 复制代码
      class Solution(object):
          def permuteUnique(self, nums):
              """
              :type nums: List[int]
              :rtype: List[List[int]]
              """
              def backtrack(path, used):
                  if len(path) == len(nums):
                      res.append(path[:])
                      return
                  for i in range(len(nums)):
                  # 剪枝条件:
                  # 1. 当前数字已使用过
                  # 2. 当前数字与前一个数字相同,且前一个数字未被使用(保证相同数字的顺序)
                      if used[i] or (i > 0 and nums[i] == nums[i-1] and not used[i-1]):
                          continue
                      used[i] = True
                      path.append(nums[i])
                      backtrack(path, used)
                      path.pop()
                      used[i] = False
      
              nums.sort()  # 必须先排序,使相同数字相邻
              res = []
              used = [False] * len(nums)
              backtrack([], used)
              return res

i > 0 and nums[i] == nums[i-1] and not used[i-1]:当前数字与前一个数字相同且前一个数字未被使用(保证相同数字按顺序使用,避免重复)

全排序1中代码直接检查num in path来判断是否使用过,这在有重复数字时会失效。


三、常见错误与调试技巧

  1. 忘记回溯

    • 错误示例 :在path.append(num)后忘记path.pop(),导致结果重复。

    • 调试方法 :在递归前后打印path,观察状态变化。

  2. 去重逻辑错误

    • 案例:LeetCode 47(含重复数字的全排列)中未排序直接跳过重复数。

    • 修正 :必须先排序,再判断nums[i] == nums[i-1]

文章感谢各个CSDN博主、b站up主等多位大佬的细心讲解,以上是我的一些笔记。

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
水w1 小时前
【Python爬虫】简单案例介绍1
开发语言·爬虫·python
Ludicrouers2 小时前
【Leetcode-Hot100】和为k的子数组
算法·leetcode·职场和发展
巨可爱熊2 小时前
高并发内存池(定长内存池基础)
linux·运维·服务器·c++·算法
FreakStudio4 小时前
一文速通 Python 并行计算:07 Python 多线程编程-线程池的使用和多线程的性能评估
python·单片机·嵌入式·多线程·面向对象·并行计算·电子diy
qq_365911604 小时前
GPT-4、Grok 3与Gemini 2.0 Pro:三大AI模型的语气、风格与能力深度对比
开发语言
爱数模的小驴4 小时前
2025 年“认证杯”数学中国数学建模网络挑战赛 C题 化工厂生产流程的预测和控制
深度学习·算法·计算机视觉
Susea&5 小时前
数据结构初阶:队列
c语言·开发语言·数据结构
慕容静漪5 小时前
如何本地安装Python Flask并结合内网穿透实现远程开发
开发语言·后端·golang
ErizJ5 小时前
Golang|锁相关
开发语言·后端·golang
GOTXX5 小时前
【Qt】Qt Creator开发基础:项目创建、界面解析与核心概念入门
开发语言·数据库·c++·qt·图形渲染·图形化界面·qt新手入门