给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1>源代码
class Solution:
def permute(self, nums):
def back(first=0):
if(first == len(nums)):
res.append(nums[:])
return
for i in range(first, len(nums)):
nums[first], nums[i] = nums[i], nums[first]
back(first+1)
nums[first], nums[i] = nums[i], nums[first]
res = []
back()
return res
2>算法介绍
这道题对于第一次遇见的我来说,还是有些没思路。对于我们人类来说很轻而易举的事情,要想交给机器来完成还是需要精心设计算法。这道题是一道经典的回溯算法题。
按照我们人类的思路,全排列[1,2,3]的思路大概是这样:
对于上述的状态1来说,你在排列2,3的时候,得到了排列后的序列1,2,3,你会发现这时这个序列已经排列完成了,这时候你就需要回溯到上一状态,那就是1作为第一个元素的状态,再去寻找此时的第二个元素应该是什么。
我们再举一个更普遍的例子,假设我们要全排列序列[3,4,5,6,7,8],在某一时刻我们已经排列到了[6,7, | 3,4,5,8]的情况,表示当前状态下,|左侧的元素位置不再变化。那么如果接下来我想从[6,7,8, |3,4,5]这个状态开始尝试,那么我们就应该把8和3的位置对调,并且将 | 向右移动。这也就是我们递归的设置思路。在你尝试完这种状态的所有排列后,别忘了进行复原,也就是将本例中将8和3的位置调换回来。
如果上述描述还不足以理解的话,我们不妨思考一下第一次递归退出点。第一次递归所获得的排列一定是原列表顺序,因为back()会不断递归进去直到最后一个元素。对于例子[1,2,3]来说,递归退出后,此时的first=1,i=2,交换两者对应的值会得到排列[1,3,2]。在输出排列后会将这两个值再换回来,并退出此时的递归。
退出递归后,first=0,i=1,交换两者对应的值得到[2,1,|3],也即[2,1,3],接下来的步骤就很好理解了。
实话说,这种题目第一次遇到还是有难度,设计递归本身就是一件比较抽象的事情,还是需要多多练习。