1. 题⽬链接:47.全排列II
2. 题⽬描述:
3. 解法:
算法思路:
因为题⽬不要求返回的排列顺序,因此我们可以对初始状态排序,将所有相同的元素放在各⾃相邻的 位置,⽅便之后操作。因为重复元素的存在,我们在选择元素进⾏全排列时,可能会存在重复排列, 例如:[1,2,1],所有的下标排列为:
按照以上下标进⾏排列的结果为:
可以看到,有效排列只有三种[1,1,2],[1,2,1],[2,1,1],其中每个排列都出现两次。因此,我们需 要对相同元素定义⼀种规则,使得其组成的排列不会形成重复的情况:
1. 我们可以将相同的元素按照排序后的下标顺序出现在排列中,通俗来讲,若元素s出现x次,则排 序后的第2个元素s⼀定出现在第1个元素s后⾯,排序后的第3个元素s⼀定出现在第2个元素s后⾯,以此类推,此时的全排列⼀定不会出现重复结果。
2. 例如:a1=1,a2=1,a3=2,排列结果为[1,1,2]的情况只有⼀次,即a1在a2前⾯,因为a2不会 出现在a1前⾯从⽽避免了重复排列。
3. 我们在每⼀个位置上考虑所有的可能情况并且不出现重复;
4. *注意*:若当前元素的前⼀个相同元素未出现在当前状态中,则当前元素也不能直接放⼊当前状态 的数组,此做法可以保证相同元素的排列顺序与排序后的相同元素的顺序相同,即避免了重复排列 出现。
5. 通过深度优先搜索的⽅式,不断地枚举每个数在当前位置的可能性,并在递归结束时回溯到上⼀个 状态,直到枚举完所有可能性,得到正确的结果。
递归函数设计:void backtrack(vector& nums, int idx)
参数:idx(当前需要填⼊的位置);
返回值:⽆;
函数作⽤:查找所有合理的排列并存储在答案列表中。
递归流程如下:
1. 定义⼀个⼆维数组ans⽤来存放所有可能的排列,⼀个⼀维数组perm⽤来存放每个状态的排列, ⼀个⼀维数组visited标记元素,然后从第⼀个位置开始进⾏递归;
2. 在每个递归的状态中,我们维护⼀个步数idx,表⽰当前已经处理了⼏个数字;
3. 递归结束条件:当idx等于nums数组的⻓度时,说明我们已经处理完了所有数字,将当前数组存 ⼊结果中;
4. 在每个递归状态中,枚举所有下标i,若这个下标未被标记,并且在它之前的相同元素被标记过, 则使⽤nums数组中当前下标的元素:
a. 将visited[i]标记为1;
b. 将nums[i]添加⾄perm数组末尾;
c. 对第step+1个位置进⾏递归;
d. 将visited[i]重新赋值为0,并删除perm末尾元素表⽰回溯;
5. 最后,返回ans。
C++算法代码:
class Solution
{
public:
vector<vector<int>>answer;
vector<int>temp; //单个序列
int key[9]={0}; //状态数组
void dfs(vector<int>& nums)
{
//出口
if(temp.size()==nums.size())
{
//查重
if (find(answer.begin(),answer.end(),temp)==answer.end())
{
answer.push_back(temp);
}
return ;
}
for(int i=0;i<nums.size();i++)
{
if(!key[i])
{
//更新状态数组
key[i]=1;
temp.push_back(nums[i]);
dfs(nums);
//恢复现场
temp.pop_back();
key[i]=0;
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums)
{
dfs(nums);
return answer;
}
};
class Solution
{
vector<int> path;
vector<vector<int>> ret;
bool check[9];
public:
vector<vector<int>> permuteUnique(vector<int>& nums)
{
sort(nums.begin(), nums.end());
dfs(nums, 0);
return ret;
}
void dfs(vector<int>& nums, int pos)
{
if (pos == nums.size())
{
ret.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++)
{
// 剪枝
if (check[i] == false && (i == 0 || nums[i] != nums[i - 1] ||
check[i - 1] != false))
{
path.push_back(nums[i]);
check[i] = true;
dfs(nums, pos + 1);
path.pop_back(); // 恢复现场
check[i] = false;
}
}
}
};
Java算法代码:
class Solution
{
List<Integer> path;
List<List<Integer>> ret;
boolean[] check;
public List<List<Integer>> permuteUnique(int[] nums)
{
path = new ArrayList<>();
ret = new ArrayList<>();
check = new boolean[nums.length];
Arrays.sort(nums);
dfs(nums, 0);
return ret;
}
public void dfs(int[] nums, int pos)
{
if (pos == nums.length)
{
ret.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++)
{
// 剪枝
if (check[i] == false && (i == 0 || nums[i] != nums[i - 1] ||
check[i - 1] != false))
{
path.add(nums[i]);
check[i] = true;
dfs(nums, pos + 1);
// 回溯 -> 恢复现场
path.remove(path.size() - 1);
check[i] = false;
}
}
}
}
标签:排列,递归,nums,元素,算法,vector,path,check
From: https://blog.csdn.net/2301_79580018/article/details/140798631