在问 · 算法题解

46. 全排列

中等回溯回溯 + used 标记数组力扣原题 ↗

题目描述

输入一个元素互不相同的整数数组 nums。

把 nums 的元素按所有可能的先后顺序排列,每种排法都用到全部元素各一次。返回所有排法,排法之间的顺序不限。

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

解释:3 个不同的数共有 3! = 6 种排法。

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

解释:两个数有 2 种排法。

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

解释:只有一个数,只有一种排法。

数据范围

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的整数互不相同。

解题思路:回溯 + used 标记数组

回溯维护 path 与 used。

为什么正确

递归返回前恢复选择。

复杂度分析

时间 O(n·n!),额外空间 O(n)。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
 def permute(self,a):
  out=[];p=[];u=[0]*len(a)
  def f():
   if len(p)==len(a):out.append(p[:]);return
   for i,x in enumerate(a):
    if not u[i]:u[i]=1;p.append(x);f();p.pop();u[i]=0
  f();return out

载入编辑器,自己跑一遍 →

其他解法

used 写法每层扫描全部 n 个元素并跳过已用元素,产出字典序(输入有序时);交换写法每层只遍历右侧未确定段,无需额外标记,但输出顺序被打乱。

解法 2:原地交换回溯 时间 O(n·n!),空间 O(n)

把数组划分为两段:下标 first 左侧是已经确定的前缀,右侧是待选元素。第 first 层依次把 nums[i](i ≥ first)交换到位置 first,递归处理 first+1,返回后再交换回来恢复现场。first 到达 n 时,整个数组就是一个排列。

不需要 used 数组和额外的路径数组,直接在原数组上生成排列,省去每层对 n 个元素的“是否已用”判断。缺点是生成顺序不是字典序;若输入有重复元素,去重也比 used 写法麻烦。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)

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

        dfs(0)
        return res

回溯 专题的其他题目

查看 回溯 全部题目 →