Leetcode 回溯 1

题号直达: 46 全排列 | 78 子集

LeetCode 46. 全排列 ↑

题目

给定一个不含重复数字的数组 nums,返回其所有可能的全排列。你可以按任意顺序返回答案。

测试样例

  • 示例 1: nums = [1,2,3]
    输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

  • 示例 2: nums = [0,1]
    输出:[[0,1],[1,0]]

  • 示例 3: nums = [1]
    输出:[[1]]

提示

  • 1 ≤ len(nums) ≤ 6
  • -10 ≤ nums[i] ≤ 10
  • nums 中整数互不相同

代码(Python):交换法回溯

固定前缀 nums[0:first],从 nums[first:] 中依次选元素换到位置 first,再递归填 first+1;递归返回后交换还原,保证同一层枚举不破坏兄弟分支。当 first == n 时,当前 nums 即一个排列,拷贝入结果。

class Solution(object):
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """

        def backtrace(first=0):
            if first == n:
                res.append(nums[:])
            for i in range(first, n):
                nums[first], nums[i] = nums[i], nums[first]
                backtrace(first + 1)
                nums[first], nums[i] = nums[i], nums[first]

        n = len(nums)
        res = []
        backtrace()
        return res

讲解视频(讲解视频文件 0405_1.mp4)

思路解析

  • 回溯:每层在 i ∈ [first, n) 上尝试把 nums[i] 换到 first,进入子问题 first+1,回溯前再换回来,避免状态串味。
  • 终止:first == n 时说明每一位都已选定,将 nums 的拷贝加入 res(须用 nums[:],勿直接 append(nums))。
  • 复杂度:共 n! 个排列,每个拷贝长度 n,时间 O(n · n!),递归栈 O(n)。

LeetCode 78. 子集 ↑

题目

给定整数数组 nums,其中元素互不相同。返回 nums 所有可能的子集(幂集)。解集不能包含重复的子集;子集顺序任意。

测试样例

  • 示例 1: nums = [1,2,3]
    输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

  • 示例 2: nums = [0]
    输出:[[],[0]]

提示

  • 1 ≤ len(nums) ≤ 10
  • -10 ≤ nums[i] ≤ 10
  • nums 中元素互不相同

代码(Python)解法一:二进制枚举(掩码)

共 2^N 个子集,用 0 … 2^N - 1 的整数 i 表示掩码:第 j 位为 1 表示子集中包含 nums[j]。将 i 转为二进制串后须左补零到长度 N(zfill(N)),再与 nums[0] … nums[N-1] 一一对应;若仅用 bin(i)[2:] 不补零,位数不足时与下标会错位。

class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        N = len(nums)
        res = []
        itera = 2 ** N
        for i in range(itera):
            mask = bin(i)[2:].zfill(N)
            temp = []
            for j in range(N):
                if mask[j] == "1":
                    temp.append(nums[j])
            res.append(temp)
        return res

思路解析(解法一)

  • 一一对应:N 个元素选或不选,共 2^N 种,与 0 … 2^N-1 的二进制表示一一对应。
  • 实现:mask[j] == '1' 则把 nums[j] 加入当前子集。
  • 复杂度:时间 O(n · 2^n),结果列表规模亦为 O(n · 2^n);空间除答案外 O(n) 临时数组。

代码(Python)解法二:DFS(选或不选)

按下标 cur 依次考虑 nums[cur]:先选(入栈后递归 cur+1,回溯 pop),再不选(直接递归 cur+1)。cur == len(nums) 时当前路径 t 即一个子集,拷贝进答案。

class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        self.t = []
        self.ans = []

        def dfs(cur):
            if cur == len(nums):
                self.ans.append(self.t[:])
                return

            self.t.append(nums[cur])
            dfs(cur + 1)

            self.t.pop()
            dfs(cur + 1)

        dfs(0)
        return self.ans

思路解析(解法二)

  • 决策树:每个元素分叉「放入当前子集 / 不放入」,叶深 n 时得到一条完整选择,共 2^n 条路径。
  • 拷贝:self.ans.append(self.t[:]) 须复制列表,否则后续修改 self.t 会污染已加入的结果。
  • 复杂度:时间 O(n · 2^n),递归栈 O(n),与解法一同阶。
posted @ 2026-10-12 00:15  Phier  阅读(2)  评论(0)    收藏  举报