LeetCode 第 46 题:“全排列”题解

题解地址:回溯算法 + 位掩码(Python 代码、Java 代码)

说明:文本首发在力扣的题解版块,更新也会在第 1 时间在上面的网站中更新,这篇文章只是上面的文章的一个快照,您可以点击上面的链接看到其他网友对本文的评论。

传送门:46. 全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

回溯算法 + 位掩码(Python 代码、Java 代码)

思路分析

这是一个非常典型的使用 回溯算法 解决的问题。解决回溯问题,我的经验是 一定不要偷懒,拿起纸和笔,画出一个树形结构,思路和代码就会比较清晰了

方法:回溯算法(深度优先遍历 + 状态重置)

以示例输入: [1, 2, 3] 为例,因为是排列问题,只要我们按照顺序选取数组,保证上一层选过的数字不在下一层出现,就能够得到不重不漏的所有排列,画出树形结构如下图:

46-1.png{:width=600} {:align=center}

注意

1、在每一层,我们都有若干条分支供我们选择。由于是排序问题,之前使用过的数字,在下一层中不能再选取,那么从当前层走到下一层的时候,我们就要问一问自己,哪些数字已经使用过。在编码实现中,可以使用一个布尔型数组 used,用于记录之前(当前路径之前的层)哪些数字使用过。

2、在程序执行到上面这棵树的叶子结点的时候,此时递归到底,方法要返回了,对于这个最后一层选取的数,要做两件事情:(1)释放对它的占用;(2)将它从当前选取放进的排列中弹出。当前,在每一层的方法执行完毕,要返回的时候,都需要这么做。这两点可以简单概括为“状态重置”。

我相信我已经成功地把大家绕晕了,以下是“排列”问题我个人觉得比较好的写法,推荐给大家。

如果序列包含重复数字,这就是 「力扣」第 47 题:全排列 II ,需要做剪枝操作,做法可以参考 回溯 + 剪枝(Python 代码、Java 代码)

参考代码 1

Python 代码:

class Solution:

    def permute(self, nums):
        if len(nums) == 0:
            return []

        used = [False] * len(nums)
        res = []
        self.__dfs(nums, 0, [], used, res)
        return res

    def __dfs(self, nums, index, pre, used, res):
        # 先写递归终止条件
        if index == len(nums):
            res.append(pre.copy())
            return

        for i in range(len(nums)):
            if not used[i]:
                # 如果没有用过,就用它
                used[i] = True
                pre.append(nums[i])

                # 在 dfs 前后,代码是对称的
                self.__dfs(nums, index + 1, pre, used, res)

                used[i] = False
                pre.pop()

Java 代码:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class Solution {


    // curSize 表示当前的路径 path 里面有多少个元素

    private void generatePermution(int[] nums, boolean[] visited, int curSize, int len, Stack<Integer> path, List<List<Integer>> res) {
        if (curSize == len) {
            // 此时 path 已经保存了 nums 中的所有数字,已经成为了一个排列
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < len; i++) {
            if (!visited[i]) {
                path.push(nums[i]);
                visited[i] = true;
                generatePermution(nums, visited, curSize + 1, len, path, res);
                // 刚开始接触回溯算法的时候常常会忽略状态重置
                // 回溯的时候,一定要记得状态重置
                path.pop();
                visited[i] = false;
            }
        }
    }

    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        boolean[] used = new boolean[len];
        if (len == 0) {
            return res;
        }
        generatePermution(nums, used, 0, len, new Stack<>(), res);
        return res;
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4};
        Solution solution = new Solution();
        List<List<Integer>> permute = solution.permute(nums);
        for (int i = 0; i < permute.size(); i++) {
            System.out.println(permute.get(i));
        }
    }
}

技巧:使用位掩码代替 used 数组

参考代码 2:

from typing import List


class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        size = len(nums)
        if size == 0:
            return []

        # used = [False] * len(nums)
        state = 0
        res = []
        self.__dfs(nums, 0, size, [], state, res)
        return res

    def __dfs(self, nums, index, size, pre, state, res):
        # 先写递归终止条件
        if index == size:
            res.append(pre[:])
            return

        for i in range(size):
            if ((state >> i) & 1) == 0:
                # 如果没有用过,就用它
                state ^= (1 << i)
                pre.append(nums[i])

                # 在 dfs 前后,代码是对称的
                self.__dfs(nums, index + 1, size, pre, state, res)
                # 状态重置
                state ^= (1 << i)
                pre.pop()


if __name__ == '__main__':
    s = Solution()
    nums = [1, 2, 3]
    result = s.permute(nums)
    print(result)

Python 代码:

from typing import List


class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        size = len(nums)
        if size == 0:
            return []

        # used = [False] * len(nums)
        state = 0
        res = []
        self.__dfs(nums, 0, size, [], state, res)
        return res

    def __dfs(self, nums, index, size, pre, state, res):
        # 先写递归终止条件
        if index == size:
            res.append(pre[:])
            return

        for i in range(size):
            if ((state >> i) & 1) == 0:
                # 如果没有用过,就用它
                pre.append(nums[i])
                # 在 dfs 前后,代码是对称的
                self.__dfs(nums, index + 1, size, pre, state ^ (1 << i), res)
                # 状态重置
                pre.pop()


if __name__ == '__main__':
    s = Solution()
    nums = [1, 2, 3]
    result = s.permute(nums)
    print(result)