每天一道LeetCode-----獲取無重複項/有重複項序列的全排列
原題連結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]);
}
}
};