題目
給定一個可包含重複數字的序列,返回所有不重複的全排列。
示例:
輸入: [1,1,2]
輸出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/permutations-ii
分析
在用幾個數字進行排列組合時,可以按照數字的大小順序進行排列。
如利用1 2 2 3 4進行排列組合。
1 2 2 3 4組成的數字最小的數爲12234,最大數字爲43221,字典序排列的關鍵在於從某一數字A查找下一個比這個數字A大的最小數字,關鍵步驟如下。
- 找到第一個大於前一個數字的位置 i
從後往前找第一個順序的數字即nums[i]>nums[i-1]。如 13422 從2開始查找,4 2 2都是非順序(從左到右從小到大),直到掃描到3 4爲順序 - 找到大於nums[i-1]的最小的數所在的位置 j
在nums[i]~nums[nums.size()-1]中找到最小的大於nums[i-1]的數,如4 2 2 中大於3的最小的數爲4記爲nums[j] - 交換nums[i-1]和nums[j]
- 翻轉子數組nums[i…numsSize-1]
由於每次選擇的是最小的大於nums[i-1]的元素與nums[i-1]交換,所以nums[i]~nums[nums.size()-1]是逆序(從大到小)排列的,將nums[i]到nums[nums.size()-1]進行reverse()就可以找到下一個數字
按照上述方法循環查找下一個數字,當達到最大值時,即所有數字逆序排列則停止尋找下一個數字。 如上例中,查找到43221時停止查找下一個數字。
代碼
class Solution {
public:
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
bool nextNum(vector<int>& nums) {
for (int i = nums.size() - 1;i > 0;i--) {
/*找到第一個大於前一個數字的位置i*/
if (nums[i] > nums[i - 1]) {
/*找到大於nums[i-1]的最小的數所在的位置j*/
for (int j = nums.size() - 1;j >= i;j--) {
if (nums[j] > nums[i - 1]) {
/*交換nums[i-1]和nums[j]*/
swap(nums[j], nums[i - 1]);
break;
}
}
/*翻轉子數組nums[i...numsSize-1]*/
reverse(nums.begin()+i,nums.end());
return true;
}
}
/*最大的數沒有下一個排列數 如4321*/
return false;
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
//vector<int> tmp;
bool flag = true;
sort(nums.begin(), nums.end());
while (flag) {
res.push_back(nums);
flag = nextNum(nums);
}
return res;
}
};
提交結果
積累
對vector、set等容器進行排序時,使用sort(nums.begin(),nums.end(),cmp);而不用nums.sort(),且在用時要incude,cmp爲比較規則。
可以使用reverse(nums.begin()+cnt,nums.end())進行數組局部逆轉。