原题链接Permutations
要求是输出给定序列的全排列,序列中不包含重复元素
STL
中有next_permutation
函数可以获取当前序列的下一个排列,使用起来也很简单,先对序列按递增顺序排序,然后不断调用next_permutation
函数获取当前序列的下一个更大的排列,如果没有更大的排列就返回false
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
std::sort(nums.begin(), nums.end());
vector<vector<int>> res;
res.emplace_back(nums);
}while(next_permutation(nums.begin(), nums.end()));
return res;
}
};
这里主要是利用另一种方法实现,做个记录
对于全排列问题,又是没有重复项的序列,可以采用不断交换两个位置的方法获得所有的排列,即以某个点为开始点,依次和后面的做交换,因为没有重复项,所以得到的序列都是不同的结果,例如
1 2 3
/*
* 开始点为第一个数
* 1和2交换 ->2 1 3
* 1和3交换 ->3 2 1
*/
2 1 3
/*
* 从2 1 3递归
* 开始点为第二个数
* 1和3交换 ->2 3 1
*/
3 2 1
/*
* 从3 2 1递归
* 开始点为第二个数
* 2和1交换 ->3 1 2
*/
/* 结果 */
1 2 3
2 1 3
2 3 1
3 1 2
3 2 1
但是这种方法少了一种结果,{1 3 2}
。因为序列{1 2 3}的时候是以1为开始点,但是没有对2和3进行交换。原因在于没有从{1 2 3}
递归并且开始点是第二个数的过程。所以,解决办法就是当开始点进行交换时,也要和自己交换一次,但是交换的结果不能添加到结果中。但是这样对于第一个序列{1 2 3}
就不在结果中,所以需要在交换前手动添加第一个序列。
代码如下
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
/* 从大到小排序 */
std::sort(nums.begin(), nums.end());
vector<vector<int>> res;
/* 添加最初的序列 */
res.push_back(nums);
get_permutation(nums, 0, res);
return res;
}
private:
void get_permutation(vector<int>& nums, int idx, vector<vector<int>>& res)
{
if(idx >= nums.size())
return;
/* 以idx位置作为开始点,依次和后面的交换 */
for(int i = idx; i < nums.size(); ++i)
{
/* 交换,获取一个排列 */
swap(nums[i], nums[idx]);
/* 如果不是自己和自己交换,就把排列添加到结果中 */
if(i != idx)
res.push_back(nums);
/* 递归调用,以下一个位置作为开始点 */
get_permutation(nums, idx + 1, res);
/* 交换完再换回来,回到开始的状态,然后和下一个交换 */
swap(nums[i], nums[idx]);
}
}
};
扩展
Permutations II
原题链接Permutations II
和上面要求一样,唯一区别是给定的序列可能有重复的元素
如果再按照上面的方法,那么会出现大量重复的结果,比如第一个1和第二个1交换,结果没有变换,但是被添加了两次。所以,在进行交换的时候,需要特别注意,以当前位置作为开始点和后面的元素进行交换时,这个元素在之前有没有交换过,如果交换过了,就不用再交换了。
例如,如果开始时序列为{1 2 7 7 9}
,某次交换以2为开始点
/* swap(nums[1], nums[2]) -> 1 7 2 7 9 */
开始点为第2个数2,2先和第一个7交换,变为1 7 2 7 9
/* swap(nums[2], nums[3]) -> 1 7 7 2 9 */
开始点变为第3个数7,7先和2交换,变为1 7 2 7 9
/* ... */
交换一轮后回到1 2 7 7 9
/* swap(nums[1], nums[3]) -> 1 7 7 2 9 */
开始点为第2个数2,2和第二个7交换,变为1 7 7 2 9
此时已经和先前的结果重复了
原因就在于如果有重复的元素,那么开始点第一次和重复元素的第一个元素交换,接着和第二个元素交换获取的排列,和开始点直接和重复元素的第二个元素交换获取的排列是一样的
所以,只需要和重复元素的第一个元素交换一次即可,后面重复的元素都不需要再次交换,因为在向后递归的过程中,开始点和后面的重复元素已经交换过了。
代码如下
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
/* 递增排序 */
std::sort(nums.begin(), nums.end());
vector<vector<int>> res;
/* 添加原始排列 */
res.push_back(nums);
get_permutation(nums, 0, res);
return res;
}
private:
void get_permutation(vector<int>& nums, int idx, vector<vector<int>>& res)
{
if(idx >= nums.size())
return;
/* 记录是否有交换过 */
std::unordered_set<int> hash;
for(int i = idx; i < nums.size(); ++i)
{
if(hash.find(nums[i]) != hash.end())
continue;
hash.insert(nums[i]);
swap(nums[i], nums[idx]);
if(i != idx)
res.push_back(nums);
get_permutation(nums, idx + 1, res);
swap(nums[i], nums[idx]);
}
}
};