小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
题目
给定一个可包含重复数字的序列 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]]
提示:
1 <= nums.length <= 8-10 <= nums[i] <= 10
思路
该题是基础全排列的变种,主要在于去重。
针对同一层次的计算,对连续的相同的元素只选取一个进行后续的替换,即可等价于基础全排列。例如,当前层次是[1,2, 1, 2], 我们可以只选取第一次出现的元素作为替换: 对于第一个元素1, 第一次出现,则其结果为1与[2, 1, 2]的所有全排列的连接,标记1已使用;对于第二个元素2,2未使用,则其结果为2与[1, 1, 2]的全排列的连接,并标记2已使用; 对于第3个元素1,其已使用,跳过;对于最后一个元素2,由于2已使用,跳过。
var swap = function(nums, i, j) {
if (i === j)
return;
const t = nums[i];
nums[i] = nums[j];
nums[j] = t;
};
var cal = function (nums, first, result) {
if (nums.length === first) {
result.push([...nums]);
return;
}
const map = new Map();
for (let i = first; i < nums.length; i++) {
if (!map.get(nums[i])) {
map.set(nums[i], true);
swap(nums, first, i);
cal(nums, first + 1, result);
swap(nums, first, i);
}
}
};
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function(nums) {
if (nums == null)
return;
nums.sort((a, b) => a - b);
const res = [];
cal(nums, 0, res);
return res;
};
优化
class Solution{
public:
vector<vector<int>> permuteUnique(vector<int>& nums)
{
vector<int> track(nums.begin(),nums.end());
sort(track.begin(),track.end());
vector<vector<int>> result;
backtrack(track,result,0,track.size());
return result;
}
/*
针对同一层次的计算,对连续的相同的元素只选取一个进行后续的替换,即可等价于基础全排列。例如,当前层次是[1,2, 1, 2], 我们可以只选取第一次出现的元素作为替换: 对于第一个元素1, 第一次出现,则其结果为1与[2, 1, 2]的所有全排列的连接,标记1已使用;对于第二个元素2,2未使用,则其结果为2与[1, 1, 2]的全排列的连接,并标记2已使用; 对于第3个元素1,其已使用,跳过;对于最后一个元素2,由于2已使用,跳过。
*/
void backtrack(vector<int>& track,vector<vector<int>>& result,int index,int size)
{
if(index==size)result.push_back(track);
else{
unordered_map<int,int> mp;
for(int i=index;i<track.size();++i)//index的起始点表示选择列表的范围
{
/*剪枝:同层次此元素已使用多次,在使用必然会照成重复全排列,所以直接跳过*/
if(mp.count(track[i]))continue;
/*决策路径加上这个决策*/
swap(track[index],track[i]);
/*进入下一步决策*/
backtrack(track,result,index+1,size);
/*决策路径移除这个决策*/
swap(track[index],track[i]);
/*标记此层次这个元素已使用一次了*/
mp[track[i]]=1;
}
}
}
};