这是我参与更文挑战的第26天,活动详情查看: 更文挑战
全排列(题号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 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同
链接
解释
这题啊,这题是经典排列组合。
还是比较简单的一题,只要考虑所有的组合就可以了,而且题目还规定了没有重复数字,这就更简单了。
官方推荐是用递归做的,每次需要注意当前数组的长度,和数字的使用状态(用过还是没用过),之后持续回溯就好,回溯的过程中注意要更新数字的使用状态,否则会导致数字使用重复或者压根不使用的情况出现。
但笔者这里并不推荐这种方法,因为相对来说还是比较复杂的,时间复杂度应该来到了O(n2),有没有一种更简单的方法呢?
这就是笔者第一时间想到的方法了,是的,笔者第一时间并没有想到回溯,而是简单粗暴的列出所有的可能性。
比方说这样的一个🌰:
[1, 2, 3]
首先拆解数组,可以分为这样的三个数组👇:
[1]
[1, 2]
[1, 2, 3]
第一个数组的话就很简单了,只有一种情况,那就是:
[1]
如果再添加一个数组呢?应该是两种情况👇:
[1, 2]
[2, 1]
这里其实是有个特征的,2
这个数字是插入在1
的前面和后面的,按照index
来说的就是0
和1
。
如果再有一个数字呢?
那就可以插入三个位置了,分别是0
、1
、2
。
也就是插入在长度的为2的数组中的头部,中间和尾部,或者也可以说插入在所有的缝隙中。
说到这里是不是豁然开朗了,是的,思路就是这么简单。
每次只需要将新的数字插入到已经存在的数组中的每个位置,生成新数组后记录下来即可,一直到最后数字用完了,那么此时就可以拿到所有的可能性了。
自己的答案(排列组合)
话不多说,看看代码👇:
var permute = function(nums) {
var res = [[nums[0]]]
nums.shift()
for (const num of nums) {
var len = res.length - 1
while (len >= 0) {
var arr = res.shift()
for (let i = 0; i <= arr.length; i++) {
arr.splice(i, 0, num)
res.push([...arr])
arr.splice(i, 1)
}
len--
}
}
return res
};
首先是初始化的赋值,由于题目规定了nums
的长度至少为1,那就直接将nums
中的第一个元素插入到res
中,同时去掉nums
中的第一个元素。
接下来开始循环剩余的nums
,每遇到一个新的数字,都将其插入到res
中所有数组的所有缝隙中去,这里有一点需要注意,由于数组是复杂对象,每次都是指向同一个内存地址,所以每次插入完新数字后要把这个数字再擦出掉,否则会出现数字重复的情况。
最后直接返回res
就行,这就是最后的答案了。
普通的解法(回溯)
回溯在上一题中刚刚说过,这里也不重复回溯的定义了。
主要的回溯逻辑就是记住当前数组的长度和所有数字的状态(用没用过),之后就可以不断调用回溯方法,完成解答👇:
var permute1 = function(nums) {
var res = []
obj = {}
function DFS(arr) {
if (arr.length === nums.length) return res.push([...arr])
for (const num of nums) {
if (obj[num]) continue
obj[num] = true
DFS(arr.concat([num]))
obj[num] = false
}
}
DFS([])
return res
};
在回溯方法中,第一行是回溯的终止条件,也就是当数组长度和nums
长度一样时,证明回溯已经到头了,已经是答案了,直接推入res
中即可。
这里还是要注意同样的问题,因为数组都是指向同一个内存地址,所以此处使用解构赋值来生成一个新数组推入到res
中,否则后序数组被修改会影响到之前到元素。
终止条件后就开始循环了,按照数字顺序来一点点进行操作,如果当前数字被用过了,就跳过了。
如果没被用过,就将其状态改为用过,然后在数组中添加当前数字,进行下一次递归。最后别忘了将数字状态重置为未使用,否则会出现数字缺失的情况。
这种方法的性能着实很差,时间和空间都在30%到40%左右,而如果使用排列组合解法,则二者都可以到达90%,足以证明官方提供的解法有时候并不是最优了,依然有提升的空间。
PS:想查看往期文章和题目可以点击下面的链接:
这里是按照日期分类的👇
经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇
有兴趣的也可以看看我的个人主页👇