「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」
题目
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例1
输入: nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
复制代码
示例2
输入: nums = [1,2,3]
输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
复制代码
题解
递归 + 回溯 + 剪枝
假如数组
递归回溯所有可能结果;可以参考下图
graph TD
1 --> 1,1 & 1,2 & 1,3
1,1 --> 1,1,1 & 1,1,2 & 1,1,3
1,2 --> 1,2,1 & 1,2,2 & 1,2,3
1,3 --> 1,3,1 & 1,3,2 & 1,3,3
2 --> 2,1 & 2,2 & 2,3
2,1 --> 2,1,1 & 2,1,2 & 2,1,3
2,2 --> 2,2,1 & 2,2,2 & 2,2,3
2,3 --> 2,3,1 & 2,3,2 & 2,3,3
3 --> 3,1 & 3,2 & 3,3
3,1 --> 3,1,1 & 3,1,2 & 3,1,3
3,2 --> 3,2,1 & 3,2,2 & 3,2,3
3,3 --> 3,3,1 & 3,3,2 & 3,3,3
递归回溯实现可以先写一份极简版代码,代码中有注释,理解起来不是很难?
var permute = function(nums) {
// 数组长度
const len = nums.length;
// 递归
dfs([]);
function dfs(path) {
// 递归出口
if (path.length === len)return
for (let i = 0; i < len; i++) {
// 统计路径上的元素并放入路径数组
path.push(nums[i]);
dfs(path);
// 回溯
path.pop();
}
}
};
复制代码
如果仅仅是这份极简版代码,实现的是全排列比如 这类的数据也会统计到,明显这种分支并不是我们想要的,如何将这种数据排除的或者在递归回溯的时候将这些分支【裁剪】掉?
增加条件
var permute = function(nums) {
// 数组长度
const len = nums.length;
// 递归
dfs([]);
function dfs(path) {
// 递归出口
if (path.length === len)return
for (let i = 0; i < len; i++) {
// 统计路径上的元素并放入路径数组
if(isCheck()){
path.push(nums[i]);
dfs(path);
// 回溯
path.pop();
}
}
}
};
复制代码
假设我们在分支递归的过程中,增加条件 , 只有符合条件的分支才允许进入下一步递归,那么是不是就可以时间【剪枝】这个功能了
isCheck如何实现?
分析:
- 数组有重复元素,
- 组成不重复的全排列
有重复元素,需要排序,如果元素前后两个元素相同,可能这个分支要被剪除,但是哪个分支被剪除呢?这里有需要一个额外空间存储之前选中的元素
var permute = function(nums) {
// 数组长度
const len = nums.length;
// 之前选中的元素
const list = Array(len).fill(0)
// 递归
dfs([]);
function dfs(path) {
// 递归出口
if (path.length === len)return
for (let i = 0; i < len; i++) {
// 统计路径上的元素并放入路径数组
if(isCheck()){
list[i] = 1 ; //当前元素已经选中
path.push(nums[i]);
dfs(path);
// 回溯
path.pop();
list[i] = 1 ;// 回溯
}
}
}
};
复制代码
所以 方法要判断,
- 当前元素之前是否选过,
- 当前元素与上一位元素是否相等?
- 相同,但是上一位元素没用到,当前元素可用
- 相同,但是上一位元素已经用过了,当前元素不可用
- 以上两个条件都不满足,当前元素可用
isCheck 代码
function isCheck(i) {
if (list[i] === 1) return false
if (i > 0 && nums[i] === nums[i - 1] && list[i - 1] === 0) return false
return true
}
复制代码
根据上述思路编辑代码如下:
完整代码如下
var permuteUnique = function (nums) {
const len = nums.length
nums.sort((a, b) => a - b)
const result = []
const list = Array(len).fill(0)
dfs([])
return result
function dfs(path) {
if (path.length === len) {
result.push([...path])
return
}
for (let i = 0; i < len; i++) {
if (isCheck(i)) {
path.push(nums[i])
list[i] = 1
dfs(path)
path.pop()
list[i] = 0
}
}
}
function isCheck(i) {
if (list[i] === 1) return false
if (i > 0 && nums[i] === nums[i - 1] && list[i - 1] === 0) return false
return true
}
}
复制代码