Leetcode 46:全排列(最详细的解法!!!)

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

示例:

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

解题思路

我们首先想到的一个简单的解法,就是通过递归回溯来解。所谓的回溯,无非就是通过暴力破解。这个问题,和之前的问题Leetcode 17:电话号码的字母组合(最详细的解法!!!)很像。因为这个问题中,我们要不断的遍历nums查看元素在之前的遍历中是不是用过了,如果用过了我们在本次的遍历中就不可以再使用了,否则的话,我们可以使用。所以我们要根据nums建立一个与之对应的used,记录我们nums中的元素是不是用过了。

class Solution:
    def _permute(self, nums, p, res, used):
        if len(p) == len(nums):
            res.append(p.copy())
            return

        for i,_ in enumerate(nums):
            if not used[i]:
                p.append(nums[i])
                used[i] = True
                self._permute(nums, p, res, used)
                p.pop()
                used[i] = False      
        
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result, used = list(), list()
        if not nums:
            return result

        for _ in enumerate(nums):
            used.append(False)
        self._permute(nums, list(), result, used)
        return result

与这种思想类似,我们还可以这样写

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res, n = list(), len(nums)
        
        def _permute(i):
            if i == n:
                res.append(nums.copy())
                return
                
            for k in range(i, n):
                nums[i], nums[k] = nums[k], nums[i]
                _permute(i + 1)
                nums[i], nums[k] = nums[k], nums[i]
        
        _permute(0)
        return res

我稍微提一下这个算法的思路,我们定义函数_permute(i)表示处理从numsi位置开始的所有全排列。那么我们只需要将第一个数与后面的数交换位置,这样我们就可以得到不同的数开头的去排列,接着递归调用_permute(i+1)即可得到第一个数之后的所有数的排列结果。

这个问题也可以直接使用递归。如果我们知道了nums[1:]的全排列结果的话,我们只要将nums[0]分别插入每个排列的全部位置即可。

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) <= 1:
            return [nums]
        
        out = []
        perms = self.permute(nums[1:])
        for perm in perms:
            for i in range(0, len(perm)+1):
                p = perm[:i] + [nums[0]] + perm[i:]
                out.append(p)
        
        return out

我们使用python自带的itertools.permutations可以快速的解决这个问题。

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        import itertools
        return list(itertools.permutations(nums))

同样的,对于递归可以解决的问题,我们都应该思考是不是可以通过迭代解决。下面这种写法,参照了itertools.permutations的实现。

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = list()
        n = len(nums)
        indices = [i for i in range(n)]
        cycles = [i for i in range(n, 0, -1)]
        result.append([nums[i] for i in indices[:n]])
        while n:
            for i in reversed(range(n)):
                cycles[i] -= 1
                if cycles[i] == 0:
                    indices[i:] = indices[i+1:] + indices[i:i+1]
                    cycles[i] = n - i
                else:
                    j = cycles[i]
                    indices[i], indices[-j] = indices[-j], indices[i]
                    result.append([nums[i] for i in indices[:n]])
                    break
            else:
                return result

我主要提一下上面算法使用的思路。首先记录123,然后交换23,得到132,记录132。然后我们将2放到list前,得到213,记录213,然后交换13,得到231,记录231。然后我们将3放到list前,得到312,记录312,然后交换12,得到321,记录321

上面这种写法有很多优点,但是我更喜欢下面这种实现,完全按照递归的思路来。

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) <= 1:
            return [nums]
        
        result = [[nums[0]]]
        index = 1
        while index < len(nums):
            tmp = []
            for perm in result:
                for i in range(index+1):
                    tmp.append(perm[:i] + [nums[index]] + perm[i:])
            result = tmp
            index += 1
        return result

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)