问题描述
给出一个具有重复数字的列表,找出列表所有不同的排列。
样例
给出列表 [1,2,2]
,不同的排列有:
[
[1,2,2],
[2,1,2],
[2,2,1]
]
递归解法:
利用回溯加递归的思想,每次将当前元素与待交换元素进行交换前,判断在当前元素到待交换元素之间是否存在元素与待交换元素相等。 若存在,则跳过待交换元素;若不存在,则进行交换,然后递归调用函数。 递归函数的出口是当待交换元素下标等于数组长度时,说明已经没有其他元素可以和当前元素交换了,递归结束。 当从递归函数返回时,再进行一次交换,也就是要进行回溯,将数组恢复原样。class Solution {
public:
/**
* @param nums: A list of integers.
* @return: A list of unique permutations.
*/
vector<vector<int> > permuteUnique(vector<int> &nums) {
vector<vector<int>> res;
help(res, nums, 0);
return res;
}
void help(vector<vector<int>> &res, vector<int>&num,int p){
if (p ==num.size()){
res.push_back(num);
} else {
for (int i = p; i<num.size(); i++) {
if(contain(num,p,i))
continue;
swap(num[i],num[p]);
help(res, num, p+1);
swap(num[i],num[p]);
}
}
}
bool contain(vector<int>& num, int p, int i){
if (i>p) {
for (int j = p; j<i; j++){
if (num[j]==num[i])
return true;
}
}
return false;
} };
非递归解法:
非递归解法主要是求出全排列的下一个排序,当下一个排序与最初的排序相同时,说明所有排序已经求得,此时可以终止算法。 那么如何求得下一个排列呢?我们通过观察原数组可以发现,如果从末尾往前看,数字逐渐变大,到了2时才减小的,然后我们再从后往前找第一个比2大的数字,是3,那么我们交换2和3,再把此时3后面的所有数字转置一下即可,步骤如下:
1 2 7 4 3 1
1 2 7 4 3 1
1 3 7 4 2 1
1 3 1 2 4 7
class Solution {
public:
/**
* @param nums: A list of integers.
* @return: A list of unique permutations.
*/
vector<vector<int> > permuteUnique(vector<int> &nums) {
vector<vector<int>> res;
vector<int> tmp = nums;
do{
res.push_back(nextPermutation(tmp));
} while (tmp != nums);
return res;
}
vector<int> nextPermutation(vector<int>& num){
for(int i=num.size()-2; i>=0; i--){
if (num[i] < num[i+1]){
int j;
for ( j=num.size()-1; j>i; j--){
if (num[j] > num[i])
break;
}
swap(num[j],num[i]);
reverse(num.begin()+i+1, num.end());
return num;
}
}
reverse(num.begin(), num.end());
return num;
} };